如何在React Native中取消Image.getSize()请求?

时间:2020-01-20 04:33:17

标签: javascript reactjs react-native

我正在使用React Native的Image.getSize(uri, (width, height) => {})方法来获取远程图像的尺寸,并使用setState()将其设置为组件的状态:

componentDidMount() {
  Image.getSize(this.props.uri, (width, height) => {
    this.setState({ width, height })
  })
}

但是,有时会在返回getSize()请求之前卸载组件,这会在调用setState()时导致以下错误:

无法在已卸载的组件上执行React状态更新。这是空操作,但它表明应用程序中发生内存泄漏。要解决此问题,请取消componentWillUnmount方法中的所有订阅和异步任务。

我了解可以跟踪组件的安装状态以防止进行setState()调用,但是显然this is considered an antipattern.

假设getSize()没有提供取消未决请求的方法,还有其他方法可以实现吗?

1 个答案:

答案 0 :(得分:1)

我遇到了同样的问题。 对我有用的是将Image.getSize包装在一个诺言中:

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 };
};

然后您可以像这样在组件中使用它:

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(); 
      }
    }
  });
}