【问题标题】:Delayed invocation useEffect causes inconsistencies of component model延迟调用useEffect导致组件模型不一致
【发布时间】:2019-11-05 07:45:46
【问题描述】:

useEffect钩子的依赖改变时,useEffect钩子的效果函数仅在渲染周期(more discussed here)之后被调用。这经常会造成 UI 因组件模型不一致而变得不一致的情况。

这是一个简单的示例,其中useEffect 用于根据inputValue 的值运行resultValue 的计算:

function App() {
  const [inputValue, incrementInputValue] = useReducer((s, _) => s + 1, 0);
  const [resultValue, setResultValue] = useState(0);

  useEffect(() => {
    // It could be an asynchronous call to business logic etc.
    setResultValue(inputValue * 2);
  }, [inputValue]);

  console.log("Render: %d * 2 = %d", inputValue, resultValue);

  return (
    <div className="App">
      <p>{inputValue} * 2 = {resultValue}</p>
      <button onClick={() => { console.log('Click'); incrementInputValue(undefined); }}>
        Increment
      </button>
    </div>
  );
}

此代码返回以下日志:

Render: 0 * 2 = 0
Click 
Render: 1 * 2 = 0 // inputValue was updated, but useEffect not called yet
Render: 1 * 2 = 2
Click 
Render: 2 * 2 = 2 // inputValue was updated, but useEffect not called yet
Render: 2 * 2 = 4

Code Sandbox

避免由于延迟调用 useEffect 引起的这种不一致的最佳方法是什么?

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    在调度一个动作 (incrementInputValue) 之后会运行一个完整的生命周期,如果不“删除”这个生命周期,您将无法使其保持一致:

    1. 点击一个按钮。
    2. 调度操作 (incrementInputValue)
    3. 重新渲染二重奏以改变状态 (inputValue)
    4. 运行 useEffect 回调二重奏到 inputValue 更改。
    5. 重新渲染二重奏以改变状态 (resultValue)

    因此,如前所述,您需要摆脱相位3

    有一些解决方案,您可以使用引用,或将其组合在单个状态中。

    const reducer = (state, increase) => ({
      inputValue: state.inputValue + increase,
      resultValue: state.resultValue + increase * 2
    });
    
    const initial = { inputValue: 0, resultValue: 0 };
    
    function App() {
      const [state, increaseResult] = useReducer(reducer, initial);
    
      useEffect(() => {
        console.log(state);
      });
    
      return (
        <div className="App">
          <p>
            {state.inputValue} * 2 = {state.resultValue}
          </p>
          <button
            onClick={() => {
              console.log('Click');
              increaseResult(1);
            }}
          >
            Increment
          </button>
        </div>
      );
    }
    

    最好的方法是什么?这取决于用例,更多的是何时以及为什么使用引用而不是状态的问题。

    在这个特定的例子中,你可以说你可以只用除法来推导出所有的逻辑:inputValue = resultValue / 2

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-23
      • 2014-04-24
      • 1970-01-01
      • 1970-01-01
      • 2011-10-24
      • 1970-01-01
      • 2016-12-19
      • 1970-01-01
      相关资源
      最近更新 更多