【问题标题】:Data fetching with React hooks cleanup the async callback使用 React 钩子获取数据清理异步回调
【发布时间】:2019-08-02 23:06:55
【问题描述】:

我开始使用新的闪亮的 React Hooks 构建我的一些新组件。但是我在我的组件中使用了很多异步 api 调用,在获取数据时我还显示了一个加载微调器。所以据我理解这个概念应该是正确的:

const InsideCompontent = props => {
   const [loading, setLoading] = useState(false);

   useEffect(() => {
     ...
     fetchData()
     ...
   },[])

   function fetchData() {
     setFetching(true);
     apiCall().then(() => {
       setFetching(false)
     })
   }
}

所以这只是我对它如何工作的初步想法。只是一个小例子。 但是,如果父组件现在改变了一个条件,即该组件在异步调用完成之前被卸载,会发生什么情况。

在我在 api 回调中调用 setFetching(false) 之前,是否可以通过某种方式检查组件是否仍然挂载?

或者我在这里遗漏了什么?

这是工作示例: https://codesandbox.io/s/1o0pm2j5yq

编辑: 这里没有真正的问题。你可以在这里试试: https://codesandbox.io/s/1o0pm2j5yq

错误来自其他原因,因此使用钩子时,您无需在更改状态之前检查组件是否已安装。

使用它的另一个原因:)

【问题讨论】:

  • @fard 没有这个关于反应钩子而不是正常的反应生命周期方法
  • 没有真正的区别,甚至答案都写在了钩子里
  • 您能否明确说明为什么取消请求对您不起作用?您是否在其他地方使用这些数据?
  • so with hooks you don't need to cancel the asynchronous callback that's what it does automatically. 这不会自动完成,我们必须自己进行清理。 (移除事件监听器,取消网络请求等)

标签: javascript reactjs react-hooks


【解决方案1】:

您可以使用useRef 挂钩来存储您喜欢的任何可变值,因此您可以在卸载组件时使用它来将变量isMounted 切换为false,并检查此变量是否为true在您尝试更新状态之前。

示例

const { useState, useRef, useEffect } = React;

function apiCall() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Foo");
    }, 2000);
  });
}

const InsideCompontent = props => {
  const [state, setState] = useState({ isLoading: true, data: null });
  const isMounted = useRef(true);

  useEffect(() => {
    apiCall().then(data => {
      if (isMounted.current) {
        setState({ isLoading: false, data });
      }
    });

    return () => {
      isMounted.current = false
    };
  }, []);
  
  if (state.isLoading) return <div>Loading...</div>
  return <div>{state.data}</div>;
};

function App() {
  const [isMounted, setIsMounted] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      setIsMounted(false);
    }, 1000);
  }, []);

  return isMounted ? <InsideCompontent /> : null;
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

【讨论】:

  • mh 有趣的方法。我对我的类组件使用了类似的方法,这将是等效的。我不知何故认为这可以用钩子更容易解决。
  • @Tholle 做出可取消的承诺是 React 人员提供的更简单、更干净的解决方案,并且它与钩子 reactjs.org/blog/2015/12/16/ismounted-antipattern.html github.com/facebook/react/issues/5465#issuecomment-157888325 完美配合
  • @fard isMounted() 方法是一种反模式,是的,但是保留一个 ref 很好。在很多情况下,引入可取消的 Promise 抽象是矫枉过正的。
  • @Tholle 我正在谈论最后一节>理想情况下,任何回调都应该在卸载之前取消。
【解决方案2】:

假设这是您遇到的错误:

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

React 会同时抱怨和提示您。如果必须卸载组件但有未完成的网络请求,则应将其取消。从useEffect 中返回一个函数是一种用于执行所需的任何类型清理的机制 (docs)。

setTimeout 为例:

const [fetching, setFetching] = useState(true);

useEffect(() => {
    const timerId = setTimeout(() => {
      setFetching(false);
    }, 4000);

    return () => clearTimeout(timerId)
  })

如果组件在回调触发之前卸载,计时器将被清除,setFetching 将不会被调用。

【讨论】:

  • 超时只是为了模拟一个api请求时间。不过谢谢
  • 是的,很清楚,我只是想演示在这种情况下如何安排清理工作。
【解决方案3】:

这是一个用于获取我们内部使用的数据的 Hook。它还允许在获取数据后对其进行操作,如果在调用完成之前进行了另一个调用,它将丢弃数据。

https://www.npmjs.com/package/use-data-hook

(如果您不想要整个包裹,也可以只include the code

^ 也可以通过简单地删除类型转换为 JavaScript。

它受到this article 的粗略启发,但具有更多功能,因此如果您不需要数据操作,您可以随时使用该文章中的解决方案。

【讨论】:

    猜你喜欢
    • 2020-08-08
    • 2020-04-22
    • 2020-02-09
    • 1970-01-01
    • 2020-02-03
    • 2020-01-17
    • 2019-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多