React Hooks - 即使状态没有改变,useEffect 也会触发

IT技术 reactjs react-hooks
2021-05-06 17:03:32

我在我的组件中设置了一个效果,如果另一个状态属性发生变化,它会改变视图。但是由于某种原因,当组件挂载时,即使 的值detailIndex没有改变,效果也会运行

const EventsSearchList = () => {
    const [view, setView] = useState('table');
    const [detailIndex, setDetailIndex] = useState(null);

    useEffect(() => {
        console.log('onMount', detailIndex);
        // On mount shows "null"
    }, []);


    useEffect(
        a => {
            console.log('Running effect', detailIndex);
            // On mount shows "null"!! Should not have run...
            setView('detail');
        },
        [detailIndex]
    );

    return <div>123</div>;

};

为什么会这样?

更新:如果不清楚,我正在尝试在组件因detailIndex更改而更新时运行效果不是在安装时。

4个回答

useEffect默认情况下,React Hooks 会在每次渲染时执行,但您可以在函数中使用第二个参数来定义何时再次执行效果。这意味着该函数总是在挂载时执行。在您的情况下,您的第二个useEffect将在开始和detailIndex更改时运行

更多信息:https : //reactjs.org/docs/hooks-effect.html

来源:

有经验的 JavaScript 开发人员可能会注意到,传递给 useEffect 的函数在每次渲染时都会有所不同。[...] 如果某些值在重新渲染之间没有改变,你可以告诉 React 跳过应用效果。为此,将数组作为可选的第二个参数传递给 useEffect: [...]

就我而言,即使我在 useEffect() 中使用了第二个参数,组件也会不断更新,并且我正在打印该参数以确保它没有改变并且没有改变。问题是我正在使用 map() 渲染组件,并且在某些情况下键发生了变化,如果键发生了变化,对于 react 来说,它是一个完全不同的对象。

您可以添加第二个守卫,检查 detailIndex 是否已从初始值更改为 -1

 useEffect(
    a => {
     if(detailIndex != -1)
      {  console.log('Running effect', detailIndex);
        // On mount shows "null"!! Should not have run...
        setView('detail');
    }},
    [detailIndex]);

确保代码只在“真实”状态更新而不是在第一次渲染时运行的一种方法是分配一个变量,该变量最初是false并且仅true 在第一次渲染之后变为,并且这个变量自然应该是一个钩子,所以它将是在组件的整个生命周期内被记录下来。

3种不同的比较useEffect

const {useEffect, useRef, useState} = React

const App = () => {
  const mountedRef = useRef()                 // ← the "flag"
  const [value, setValue] = useState(false)   // simulate a state change 
  

  // with the trick
  useEffect(() => {
      if (mountedRef.current){                // ← the trick
          console.log("trick: changed")
      }
  }, [value])


  // without the trick
  useEffect(() => {
     console.log("regular: changed")
  }, [value])


  // fires once & sets "mountedRef" ref to "true"
  useEffect(() => {
    console.log("rendered")
    mountedRef.current = true
    // update the state after some time
    setTimeout(setValue, 1000, true)
  }, [])
    
  return null
}

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