【问题标题】:Correct way to cleanup useEffect with Promise使用 Promise 清理 useEffect 的正确方法
【发布时间】:2021-07-28 14:10:42
【问题描述】:

我有 useEffect 回调,我正在从多个 API 获取一些数据。为此,我使用Promise,我的代码如下所示:

useEffect(() => {
   const fetchAllData = async () => {
      const resourceOne = new Promise((resolve) => {
         // fetching data from resource one and changing some states
      })
      const resourceTwo = new Promise((resolve) => {
         // fetching data from resource two and changing some states
      })
      const resourceThree = new Promise((resolve) => {
         // fetching data from resource three and changing some states
      })

      await Promise.all([resourceOne, resourceTwo, resourceThree])
      .then(() => {
         // changing some states
      })
   }

   return fetchAllData()
},[])

在这种情况下我如何理解 useEffect 在获取所有数据之前卸载然后给我一个警告

警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。

那么如何编写正确的清理代码来避免这个警告呢?

【问题讨论】:

标签: reactjs promise use-effect


【解决方案1】:

您可以使用布尔值并在清理函数中切换它(在传递给useEffect 的回调中返回的函数)

useEffect(() => {
    let shouldUpdate = true;

    const fetchAllData = async () => {
       const resourceOne = new Promise((resolve) => {
          // fetching data from resource one and changing some states
       })
       const resourceTwo = new Promise((resolve) => {
          // fetching data from resource two and changing some states
       })
       const resourceThree = new Promise((resolve) => {
          // fetching data from resource three and changing some states
       })
 
       await Promise.all([resourceOne, resourceTwo, resourceThree])
       .then(() => {
           if(shouldUpdate) {
               // update state
           }
       })
    }
 
    fetchAllData()
    return () => {
        shouldUpdate = false;
    }
 },[])

如果组件被卸载,清理函数将被调用,shouldUpdate 将变为 false。当 promise 解决时,状态不会更新,因为 shouldUpdate 不再为真。

【讨论】:

    【解决方案2】:

    你可以使用Abort Signal,中止所有的promise,

    const controller = new AbortController();
    
    const fetch = new Promise((resolve, reject) => {
      // fetch data here
      controller.signal.addEventListener('abort', () => reject());
    });
    
    controller.abort(); // promise is cancelled
    

    翻译成您的用例:

    const controller = new AbortController();
    useEffect(() => {
       const fetchAllData = async () => {
          const resourceOne = new Promise((resolve, reject) => {
             // fetching data from resource one and changing some states
              controller.signal.addEventListener('abort', () => reject());
          })
          const resourceTwo = new Promise((resolve, reject) => {
             // fetching data from resource two and changing some states
              controller.signal.addEventListener('abort', () => reject());
          })
          const resourceThree = new Promise((resolve, reject) => {
             // fetching data from resource three and changing some states
              controller.signal.addEventListener('abort', () => reject());
          })
    
          await Promise.all([resourceOne, resourceTwo, resourceThree])
          .then(() => {
             // changing some states
          })
       }
    
       fetchAllData();
    
       return () => {
            controller.abort(); // all promise are cancelled where 'abort' listener is specified
        }
    },[])
    

    并调用controller.abort() 取消一个或多个在组件卸载时包含事件侦听器的promise。

    当调用 abort() 时,promise 会以 AbortError 拒绝,因此您可以聆听并以不同的方式处理中止的拒绝。

    【讨论】:

      猜你喜欢
      • 2021-04-23
      • 2022-07-16
      • 2018-06-19
      • 2019-10-20
      • 2019-05-17
      • 2020-09-25
      • 2021-12-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多