likes
comments
collection
share

为什么我的useEffect钩子在React中运行了两次

作者站长头像
站长
· 阅读数 13

作为React严格模式的一部分,某些生命周期函数将被运行两次,例如传递给 使用状态, useMemo使用减速器的函数,或者一个功能组件的整个主体,这可能包括你的 useEffect钩子。 如果你不熟悉在React中使用钩子,请查看我们的教程

虽然你可以禁用这一点,而且我仍然会解释如何禁用,但我考虑避免这样做,原因就在这里。如果你想跳过解释,请随时跳到最后一节,以获得快速解决方案。

副作用

在React中,你希望你的组件是纯粹的,没有不可预知的副作用。在React中,副作用是指你的函数或组件影响到它们自己以外的东西。

React希望你的函数是 "纯粹的",例如,用相同的参数,它们有相同的结果。出于这个原因,它希望能够调用你的钩子两次,并且它们应该有相同的结果。 即使它们有一个副作用,比如执行一个API调用,它也应该导致两次相同的结果。

这是因为在严格模式之外,React可能会多次运行你的钩子,因为它把渲染阶段分成几个部分,并可能暂停或重新启动工作。解决这个问题的最好方法是在编写组件时考虑到这一点,或者明确地检查你的钩子何时被运行。

解决方法

在某些情况下,你可能真的只想要一个钩子运行一次。例如,如果你使用Spotify的API,这可能是有用的,在那里你发送一个一次性的代码,以收到一个访问令牌。为了确保这一点,你可以使用useRef钩子,你可以在这里读到更多关于它的信息,来跟踪是否运行你的 useEffect.

这里有一个简单的例子,我们的 ***fetchData()***函数只是在运行后更新一个计数器。默认情况下,我们的计数器会被更新两次,这并不是我们想要的:


function App() {
  const [counter, setCounter] = useState(0);

  const fetchData = () => {
        console.log('Fetching data...');
        setCounter((oldValue) => oldValue+1);
    }

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

  return (
    <div className="App">
        <h1>Times Fetched:</h1>
      <h1>{counter}</h1>
    </div>
  );
}

为什么我的useEffect钩子在React中运行了两次

为了解决这个问题,我们用 useRef来跟踪一个布尔值。如果它是真的,我们就已经在这里了,我们可以返回。如果它是假的,我们可以执行我们的获取,然后更新引用。

function App() {
  const [counter, setCounter] = useState(0);
  const dataFetchedRef = useRef(false);

  const fetchData = () => {
        console.log('Fetching data...');
        setCounter((oldValue) => oldValue+1);
    }

  useEffect(() => {
      if (dataFetchedRef.current) return;
      dataFetchedRef.current = true;
      fetchData();
  },[])

  return (
    <div className="App">
        <h1>Times Fetched:</h1>
      <h1>{counter}</h1>
    </div>
  );
}

为什么我的useEffect钩子在React中运行了两次

顺便说一下,如果你想看更多关于React中的实际数据获取的教程,请看这个教程

禁用React严格模式

如果你使用create-react-app创建了你的React应用,你很可能在你的index.js文件里有这个,或者类似的东西:

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>

);

简单地删除 ***<React.StrictMode >。***标签周围的 ***<应用程序 >***标签,这样就可以禁用你的应用程序的严格模式了你也可以只在你希望启用严格模式的页面中包含这个标签,以便逐页选择。

另一个需要注意的是,你只有在开发模式下才会遇到这个问题--在你的应用程序的生产构建中,严格模式将被禁用,钩子将不会明确地运行两次。就像我说的,我建议保持严格模式的启用,即使它导致你的效果运行两次,因为它更有可能帮助捕捉你的应用程序中的实际问题,但选择权在你手上

希望现在我已经解决了你的问题,如果是这样,请在下面留言!如果你有任何建议,请告诉我。如果你有任何建议,或者你只是喜欢这篇文章,请告诉我!

转载自:https://juejin.cn/post/7176274043597750328
评论
请登录