I'm using React Native's Image.getSize(uri, (width, height) => {})
method to get the dimensions of a remote image and set it to a component's state with setState()
:
componentDidMount() {
Image.getSize(this.props.uri, (width, height) => {
this.setState({ width, height })
})
}
However, sometimes the component unmounts before the getSize()
request has returned, and this leads to the following error when setState()
is called:
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
I understand that it's possible to keep track of a component's mounted state to prevent the setState()
call, but apparently this is considered an antipattern.
Is there any other way that this can be achieved, given that getSize()
does not provide a way to cancel a pending request?
I encountered the same problem.
What worked for me was wrapping Image.getSize
inside a promise:
export type CancelPromise = ((reason?: Error) => void) | undefined;
export type ImageSize = { width: number; height: number };
interface ImageSizeOperation {
start: () => Promise<ImageSize>;
cancel: CancelPromise;
}
const getImageSize = (uri: string): ImageSizeOperation => {
let cancel: CancelPromise;
const start = (): Promise<ImageSize> =>
new Promise<{ width: number; height: number }>((resolve, reject) => {
cancel = reject;
Image.getSize(
uri,
(width, height) => {
cancel = undefined;
resolve({ width, height });
},
error => {
reject(error);
}
);
});
return { start, cancel };
};
Then you could use it in your component like this:
const A: React.FC = () => {
const [size, setSize] = useState<{width: number, height: number} | undefined>(});
useEffect(() => {
let cancel: CancelPromise;
const sideEffect = async (): Promise<void> => {
try {
const operation = getImageSize(uri);
cancel = operation.cancel;
const imageSize = await operation.start();
setSize(imageSize);
} catch(error) {
if (__DEV__) console.warn(error);
}
}
sideEffect();
return () => {
if (cancel) {
cancel();
}
}
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With