简体   繁体   English

由 UseEffect 调用的 UseState 不会使用 Set 方法更新变量

[英]UseState called by UseEffect doesn't update the variable using the Set method

Consider the code :考虑代码:

import React, { useState, useEffect } from 'react';
........ More stuff
const ProductContext = React.createContext();
const ProductConsumer = ProductContext.Consumer;


const ProductProvider = ({ children }) => {
  const [state, setState] = useState({
    sideBarOpen: false,
    cartOpen: true,
    cartItems: 10,
    links: linkData,
    socialIcons: socialData,
    cart: [],
    cartSubTotal: 0,
    cartTax: 0,
    cartTotal: 0,
    .......
    loading: true,
    cartCounter: 0,
  });
  
   const getTotals = () => {
    // .. Do some calculations .... 
    
    return {
      cartItems,
      subTotal,
      tax,
      total,
    };
  };
  
  
 const addTotals = () => {
    const totals = getTotals();
    setState({
      ...state,
      cartItems: totals.cartItems,
      cartSubTotal: totals.subTotal,
      cartTax: totals.tax,
      cartTotal: totals.total,
    });
  };
  
  
   /**
   * Use Effect only when cart has been changed
   */
  useEffect(() => {
    if (state.cartCounter > 0) {
      addTotals();
      syncStorage();
      openCart();
    }
  }, [state.cartCounter]);
  
  
  
  
  ..... More code 
  
  
  
    return (
    <ProductContext.Provider
      value={{
        ...state,
       ............... More stuff 
      }}
    >
      {children}
    </ProductContext.Provider>
  );
};

export { ProductProvider, ProductConsumer };

This is a Context of a Shopping cart ,whenever the user add a new item to the cart this piece of code runs :这是购物车的上下文,每当用户向购物车添加新项目时,这段代码就会运行:

  useEffect(() => {
    if (state.cartCounter > 0) {
      addTotals();
      syncStorage();
      openCart();
    }
  }, [state.cartCounter]);

And updates the state , however the setState function doesn't update state when running :并更新 state ,但是setState函数在运行时不会更新state

 setState({
      ...state,
      cartItems: totals.cartItems,
      cartSubTotal: totals.subTotal,
      cartTax: totals.tax,
      cartTotal: totals.total,
    });

Inside addTotals , even though this function is being called automatically when UseEffect detects that state.cartCounter has been changed.addTotals ,即使当 UseEffect 检测到state.cartCounter已更改时会自动调用此函数。

Why aren't the changes being reflected in the state variable ?为什么更改没有反映在state变量中?

Without a stripped down working example, I can only guess at the problems...没有精简的工作示例,我只能猜测问题......

Potential Problem 1潜在问题 1

You're calling a callback function in useEffect which should be added to it's [dependencies] for memoization.您正在调用useEffect的回调函数,应将其添加到它的[dependencies]以进行记忆。

const dep2 = React.useCallback(() => {}, []);

useEffect(() => {
   if(dep1 > 0) {
     dep2();
   }
}, [dep1, dep2]);

Since dep2 is a callback function, if it's not wrapped in a React.useCallback , then it could potentially cause an infinite re-render if it's changed.由于dep2是一个回调函数,如果它没有包含在React.useCallback ,那么如果它被更改,它可能会导致无限重新渲染。

Potential Problem 2潜在问题 2

You're mutating the state object or one of its properties.您正在改变状态对象或其属性之一。 Since I'm not seeing the full code, this is only an assumption.由于我没有看到完整的代码,这只是一个假设。 But Array methods like: splice , push , unshift , shift , pop , sort to name a few cause mutations to the original Array.但是 Array 方法如: splicepushunshiftshiftpopsort仅举几例会导致原始 Array 发生突变。 In addition, objects can be mutated by using delete prop or obj.name = "example" or obj["total"] = 2 .此外,可以使用delete propobj.name = "example"obj["total"] = 2 Again, without the full code, it's just a guess.同样,没有完整的代码,这只是一个猜测。

Potential Problem 3潜在问题 3

You're attempting to spread stale state when it's executed.您试图在执行时传播陈旧状态。 When using multiple setState calls to update an object, there's no guarantee that the state is going to be up-to-date when it's executed.当使用多个setState调用来更新一个对象时,不能保证state在执行时是最新的。 Best practice is to pass setState a function which accepts the current state as an argument and returns an updated state object:最佳实践是向 setState 传递一个函数,该函数接受当前状态作为参数并返回更新的状态对象:

setState(prevState => ({
  ...prevState,
  prop1: prevState.prop1 + 1
}));

This ensures the state is always up-to-date when it's being batch executed.这确保状态在批处理执行时始终是最新的。 For example, if the first setState updates cartTotal: 11 , then prevState.cartTotal is guaranteed to be 11 when the next setState is executed.例如,如果第一个 setState 更新了cartTotal: 11 ,那么在执行下一个 setState 时, prevState.cartTotal保证为 11 。

Potential Problem 4潜在问题 4

If state.cartCounter is ever updated within this component, then this will cause an infinite re-render loop because the useEffect listens and fires every time it changes.如果state.cartCounter在这个组件中被更新,那么这将导致无限重新渲染循环,因为useEffect每次更改时都会监听并触发。 This may or may not be a problem within your project , but it's something to be aware of.这在您的项目中可能是也可能不是问题,但这是需要注意的。 A workaround is to trigger a boolean to prevent addTotals from executing more than once.解决方法是触发布尔值以防止addTotals执行多次。 Since the prop name "cartCounter" is a number and is rather ambiguous to its overall functionality, then it may not be the best way to update the cart totals synchronously.由于道具名称“cartCounter”是一个数字并且对其整体功能相当模糊,因此它可能不是同步更新购物车总数的最佳方式。

  React.useEffect(() => {
    if (state.cartCounter > 0 && state.updateCart) {
      addTotals();
      ...etc
    }
  }, [state.updateCart, state.cartCounter, addTotals]);

Working demo (click the Add to Cart button to update cart state):工作演示(单击Add to Cart按钮以更新购物车状态):

编辑 busy-keller-4fl46


If neither of the problems mentioned above solves your problem, then I'd recommend creating a mwe .如果上述问题都不能解决您的问题,那么我建议您创建一个mwe Otherwise, it's a guessing game.否则就是猜谜游戏。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM