且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

useEffect 不会针对依赖项数组中的每个更改运行

更新时间:2022-06-16 09:22:01

问题在于 useState 的工作方式与您想象的不同.在消费者内部,您连续三次同步调用 api.addExpense(...),期望调用 setState 将更新 state> 就地 - 它不会,状态仅在下一次渲染调用时更新,而不是在第 40 行,因为您的 console.log 表明您期待它.

The problem is that useState is working differently than you think it does. Inside the consumer you're calling api.addExpense(...) three times in a row synchronously with the expectation that calling setState will update state in-place - it doesn't, the state is only updated on the next render call and not at line 40 as your console.log there suggests you're expecting it to.

因此,不是您期望的三个完整状态更新(每个 expense 一个),您只能从 {id: "d6109eb5-9d7b-4cd3-8daa-ee485b08361b", name: "book", category: "personal care", value: 200},没有任何变化,仍然基于原始状态而不是任何地方第一项的值已设置为 800.

So instead of the three complete state updates you're expecting (one for each expense) you only get the state from the last update for {id: "d6109eb5-9d7b-4cd3-8daa-ee485b08361b", name: "book", category: "personal care", value: 200}, which has no changes and is still based on the original state not one where the first item's value has been set to 800.

我建议修改您的 ExpenseState api 以获取一系列费用,以便它可以适当地处理多个内部更新.

I'd suggest modifying your ExpenseState api to take an array of expenses so it can handle multiple internal updates appropriately.


  function addOrUpdateExpense(state, { id, name, category, value }) {
    const expense = state.find(exp => exp.id === id);
    if (!expense) {
      return [...state, { id, name, category, value }];
    } else {
      const updatedExpense = state.map(exp => {
        if (exp.id === expense.id) {
          return {
            ...exp,
            ...{ id, name, category, value }
          };
        } else {
          return exp;
        }
      });

      return updatedExpense;
    }
  }

  function addExpenses(expenses) {
    let newState = [...state];
    for (const expense of expenses) {
      newState = addOrUpdateExpense(newState, expense);
    }
    setState(newState);
  }

  // ...usage

  function doIt() {
    api.addExpenses(expenses);
  }

这是一个常见的误解,请记住下一个状态只能在下一个渲染阶段异步可用,将 useState 中的状态视为在渲染过程中不可变.

This is a common misconception, just remember that the next state is only available asynchronously in the next render phase, treat state from useState as immutable within a render pass.