useEffect 不会针对依赖项数组中的每个更改运行 [英] useEffect is not running for every change in dependency array

查看:51
本文介绍了useEffect 不会针对依赖项数组中的每个更改运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我更新费用时,它不会触发 useEffect 将数据存储在本地存储中.

When I update an expense, it doesn't trigger the useEffect to store the the data in local storage.

codesandbox

import React, { useState, useEffect } from 'react';

export const ExpenseContext = React.createContext();

const ExpenseState = (props) => {
   const [state, setState] = useState(
      () => JSON.parse(localStorage.getItem('expenses')) || []
);

useEffect(() => {
    console.log('didUpdate', state)
    state.length !== 0 && localStorage.setItem('expenses', JSON.stringify(state));
}, [state]) 

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

        setState(updatedExpense)
    }
    console.log(state)
}



return (
    <ExpenseContext.Provider
        value={{
            expenses: state,
            addExpense: addExpense,
        }}
    >
        {props.children}
    </ExpenseContext.Provider>
)
}

我正在为数组的每个元素调用 addExpense.假设我们有 4 项费用存储在仓库中.现在我们更新了一项费用,然后运行以下代码.

I am calling the addExpense for every elements of the array. Assume we have 4 expenses stored in storage. Now we have updated one expense and then running the below code.

for(const expense of expenses){
        expenseContext.addExpense(expense);
    }

现在使用效果只会在最后一个元素上触发,费用也不会在上下文中更新.

Now the use effect only get triggered for the last element and the expense also not getting updated in context.

推荐答案

问题在于 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.

这篇关于useEffect 不会针对依赖项数组中的每个更改运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆