【问题标题】:Clean up async function in an useEffect React hook在 useEffect React 钩子中清理异步函数
【发布时间】:2020-08-08 05:44:15
【问题描述】:

我有以下 useEffect 函数,并试图在组件卸载时找到清理它的最佳方法。

我认为最好遵循 React docs 中的 makeCancelable,但是,当 Promise 被取消时,代码仍然会执行。

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then(
      val => hasCanceled_ ? reject({isCanceled: true}) : resolve(val),
      error => hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};
//example useEffect
useEffect(() => {
  const getData = async () => {
    const collectionRef_1 = await firestore.collection(...)
    const collectionRef_2 = await firestore.collection(...)
    if (collectionRef_1.exists) {
      //update local state
      //this still runs!
    }
    if (collectionRef_2.exists) {
      //update local state
      //and do does this!
    }
  }
  const getDataPromise = makeCancelable(new Promise(getData))
  getDataPromise.promise.then(() => setDataLoaded(true))
  return () => getDataPromise.cancel()
}, [dataLoaded, firestore])

我也尝试过const getDataPromise = makeCancelable(getData),但没有任何运气。代码执行得很好,只是在组件卸载时没有正确清理。

我还需要取消这两个等待功能吗?

【问题讨论】:

  • makeCancelable(new Promise(getData)) 应该是makeCancelable(getData()) 通知函数调用

标签: reactjs async-await react-hooks


【解决方案1】:

在您的makeCancelable 函数中,您只是检查hasCanceled_ 的值在承诺完成后(意味着getData 已经完全执行):

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    // AFTER PROMISE RESOLVES (see following '.then()'!), check if the 
    // react element has unmount (meaning the cancel function was called). 
    // If so, just reject it
    promise.then(
      val => hasCanceled_ ? reject({isCanceled: true}) : resolve(val),
      error => hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

相反,在这种情况下,我会建议您采用更简单、更经典的解决方案,并使用 isMounted 变量来创建您想要的逻辑:

useEffect(() => {
  let isMounted = true
  const getData = async () => {
    const collectionRef_1 = await firestore.collection(...)
    const collectionRef_2 = await firestore.collection(...)
    if (collectionRef_1.exists && isMounted) {
      // this should not run if not mounted
    }
    if (collectionRef_2.exists && isMounted) {
      // this should not run if not mounted
    }
  }
  getData().then(() => setDataLoaded(true))
  return () => {
    isMounted = false
  }
}, [dataLoaded, firestore])

【讨论】:

  • 啊,我明白你现在的意思了,之后检查它,而不是在此期间取消它。我将尝试重新设计我的 useEffect 以使其正常工作。 React 声明执行 isMounted 检查是一种试图避免这样做的反模式。
  • 也许这些文档只适用于类组件?因为您可以在 Relay 文档 relay.dev/docs/en/experimental/… 中看到 Facebook 使用钩子实现这种“反模式”
  • 我认为你可能是对的。文档中的示例是使用类!
  • 顺便说一句,在自己的 React 文档中找到了相同的模式(命名为ignore 而不是 isMounted):reactjs.org/docs/…
  • 太棒了,我已经调整了我的功能以使用这种方法,并且效果很好。我在问题中添加了一个链接,表明反应在哪里表明这是一种反模式。但正如您所提到的,越来越清楚的是,这只是为了上课。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-02
  • 2020-02-09
  • 2022-01-07
  • 1970-01-01
  • 2022-09-23
  • 1970-01-01
  • 2020-10-07
相关资源
最近更新 更多