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

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

考虑代码:

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 };

这是购物车的上下文,每当用户将新商品添加到购物车时 这段代码运行:

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

并更新状态,但是 setState 函数不会更新 state 当 运行 :

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

addTotals 内部,即使当 UseEffect 检测到 state.cartCounter 已更改时自动调用此函数。

为什么更改没有反映在 state 变量中?

没有精简的工作示例,我只能猜测问题...

潜在问题 1

您正在 useEffect 中调用回调函数,应将其添加到 [dependencies] 中以备记忆。

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

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

由于 dep2 是一个回调函数,如果它没有包含在 React.useCallback 中,那么它可能会导致无限 re-render 如果它被更改。

潜在问题 2

您正在改变状态对象或其属性之一。由于我没有看到完整的代码,这只是一个假设。但是 Array 方法,如:splicepushunshiftshiftpopsort 等等,会导致原始数组发生突变.此外,可以使用 delete propobj.name = "example"obj["total"] = 2 来改变对象。同样,没有完整的代码,这只是一个猜测。

潜在问题 3

您正试图在执行时传播陈旧状态。当使用多个 setState 调用来更新对象时,无法保证 state 在执行时会成为 up-to-date。最佳做法是向 setState 传递一个接受当前状态作为参数的函数和 returns 一个更新的状态对象:

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

这可以确保批处理时状态始终为up-to-date。例如,如果第一个setState更新cartTotal: 11,那么下一个setState执行时prevState.cartTotal保证为11。

潜在问题 4

如果 state.cartCounter 曾经在此组件中更新过,那么这将导致无限的 re-render 循环,因为 useEffect 会在每次更改时进行监听和触发。 这可能是也可能不是您项目中的问题,但需要注意这一点。解决方法是触发一个布尔值以防止 addTotals 执行多次。由于道具名称“cartCounter”是一个数字并且与其整体功能相当模糊,因此它可能不是同步更新购物车总数的最佳方式。

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

工作演示(点击Add to Cart按钮更新购物车状态):


如果上述两个问题都不能解决您的问题,那么我建议您创建一个 mwe。否则就是猜谜了。