React-useEffect()用于声明性状态逻辑时会导致级联渲染

时间:2019-11-18 01:16:46

标签: reactjs react-hooks

我已经很长时间享受React Hooks了。它们提供了以非常清晰和简洁的方式编写声明性逻辑的机会。例如,考虑使用此自定义useHistory钩子,该钩子记录传入的值的历史记录。

const useHistory = val => {
  const [history, setHistory] = useState([]);

  useEffect(() => {
    setHistory(history => {
      return [...history, val];
    });
  }, [val]);

  return history;
};

可以这样使用它来显示一段时间内所有先前的“计数”值(Code sandbox):

function App() {
  const [count, setCount] = useState(0);
  const history = useHistory(count);

  const historyStr = history.join(", ");

  return (
    <div className="App">
      <div>History: {historyStr}</div>

      <button onClick={() => setCount(count - 1)}>Decrement</button>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

该应用程序运行正常。但是,它有一个隐藏的缺陷。由于useEffect挂钩在渲染之后称为 ,因此,每次单击按钮时,应用都会渲染两次。 这只是一个玩具的例子。在大型应用程序中,useEffect依赖项的复杂链会导致级联渲染,这绝对会降低性能。

useEffect似乎主要是为副作用而设计的,documentation中没有太多内容可以表明是否可以接受useEffect挂钩中的更新状态。

话虽这么说,使用useEffect钩子来处理状态逻辑所提供的表现力(就像我们在useHistory示例中看到的那样)非常吸引人,并且(似乎)不可能使用其他解决方案进行复制。

我想念什么吗?有什么方法可以实现这种模式而又不会导致级联渲染?

2 个答案:

答案 0 :(得分:0)

传递给useEffect挂钩的函数将在每个渲染周期执行。加上onClick事件,将调用useState,该结果将渲染组件(第一个渲染),然后将执行useEffect函数,该函数将更改状态,然后再次渲染组件。因此,我认为您应该避免在两个函数中更新同一事件的状态。在我看来,useEffect对于诸如发送http请求之类的副作用很有用,而不是为了更新用户操作的状态。

我建议作为解决方案:

function App() {
  // Not necessary to save count because count = history[history.length - 1]

  const [history, setHistory] = useState([]);

  const historyStr = history.join(", ");
  console.log("render");

  return (

    <div className="App">
      <div>History: {historyStr}</div>

      <button
        onClick={() => setHistory(prev => [...prev, prev[prev.length - 1] - 1 ])}>
        Decrement
        </button>
      <button onClick={() => setHistory(prev => [...prev, prev[prev.length - 1] + 1 ])}>Increment</button>
    </div>
  );
}

答案 1 :(得分:0)

这是我看到的内容:单击按钮时,它会更改状态(计数)并重新渲染组件。重新渲染后,跟随[val]变化的useEffect会看到[val]变化,并在内部调用setHistory。这会再次更改您的状态(历史记录)并重新渲染您的组件。我认为,不管使用useEffect,由于您的应用程序设计,您都必须渲染两次:您的第一个状态更改将触发另一个状态更改。每个状态更改=组件都会重新呈现。