React useContext & useEffect 显示陈旧数据

IT技术 reactjs
2021-05-05 08:45:06

https://codesandbox.io/s/react-usecontextuseeffect-stale-data-bug-l81sn

在一个组件中useEffect,当某个值发生变化时,会做一些事情。现在它只是一个简单计数器的值,但在现实世界中它将是一个数组,其中删除或添加项目等。稍微复杂一点。

useEffect我也有一个调整大小检测。同样,在这个例子中不是很有趣。

const App = props => {
  const [count, dispatch] = useReducer((state, action) => {
    switch (action.type) {
      case 'INCREMENT':
        return state + 1;
      case 'DECREMENT':
        return state - 1;
      default:
        return state;
    }
  }, 0);
  return (
    <CountContext.Provider value={{ count, dispatch }}>
      <div className="App">
        <h1>App</h1>
        <Counter />
      </div>
    </CountContext.Provider>
  );
};

const Counter = () => {
  const counter = useContext(CountContext);

  useEffect(() => {
    window.addEventListener('resize', () => {
      console.log(counter.count);
    })
  },[counter])

  return (
    <div className="Counter">
      <p>Counter: {counter.count}</p>
      <input
        type="button"
        value="+"
        onClick={() => counter.dispatch({ type: 'INCREMENT' })}
      />
      <input
        type="button"
        value="-"
        onClick={() => counter.dispatch({ type: 'DECREMENT' })}
      />
    </div>
  );
};

问题是,当我调整视口大小时,会console.log(counter.count)显示所有以前的值:

在此处输入图片说明

2个回答

问题是 useEffect() 方法中的内存泄漏。您需要清理重新渲染。我分叉了你的沙箱并用它测试了它,它按预期工作:

useEffect(() => {
    const resizeEvent = () => {
      console.log(counter.count);
    };
    window.addEventListener("resize", resizeEvent);
    return () => {
      window.removeEventListener("resize", resizeEvent);
    };
  }, [counter]);

请注意清理和重构代码到被调用函数的返回,以便可以在卸载时正确删除它。

每次状态更改时,您都会添加一个新的 eventListener。三个变化 - 三个事件监听器。此外,当您的 Counter 组件被卸载时,监听器会保持活动状态,从而导致内存泄漏。首先,你可以把这部分放在 useEffect 之外:

window.addEventListener('resize', () => {
      console.log(counter.count);
    })

或者您应该在 useEffect 中使用空数组作为依赖项列表,然后它只会触发一次:

useEffect(() => {

}, []) // empty array here says 'do it once'

最后,useEffect 是获取数据或订阅事件等的理想场所。但不要忘记在不再需要组件后清除所有内容。为此,请在 useEffect 中返回清理函数:

useEffect(() => {
  // your main logic here
  ...
  // cleaning up function:
  return () => {
    removeEventListener, unsubscribe etc...
  }

}, [])