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

时间:2020-09-25 17:57:03

标签: javascript reactjs react-hooks use-effect use-state

考虑代码:

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 个答案:

答案 0 :(得分:1)

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

潜在问题1

您正在useEffect中调用一个回调函数,应将该回调函数添加到[dependencies]中以进行记忆。

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

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

由于dep2是一个回调函数,如果未将其包装在React.useCallback中,则更改后可能会导致无限次重新渲染。

潜在问题2

您正在突变状态对象或其属性之一。由于我没有看到完整的代码,因此这只是一个假设。但是Array方法,例如:splicepushunshiftshiftpopsort,仅举几例引起了原始Array的突变。另外,可以使用delete propobj.name = "example"obj["total"] = 2对对象进行突变。同样,没有完整的代码,这只是一个猜测。

潜在问题3

您试图在执行时传播陈旧状态。当使用多个setState调用来更新对象时,不能保证state在执行时将是最新的。最佳实践是向setState传递一个函数,该函数接受当前状态作为参数并返回更新的状态对象:

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

这可确保在批量执行状态时始终保持最新状态。例如,如果第一个setState更新cartTotal: 11,那么在执行下一个setState时,保证prevState.cartTotal为11。

潜在问题4

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

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

正在运行的演示(点击Add to Cart按钮以更新购物车状态):

Edit busy-keller-4fl46


如果上述任何一个问题都不能解决您的问题,那么建议您创建一个mwe。否则,这是一个猜谜游戏。