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 方法,如:splice
、push
、unshift
、shift
、pop
、sort
等等,会导致原始数组发生突变.此外,可以使用 delete prop
或 obj.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。否则就是猜谜了。
考虑代码:
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 方法,如:splice
、push
、unshift
、shift
、pop
、sort
等等,会导致原始数组发生突变.此外,可以使用 delete prop
或 obj.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。否则就是猜谜了。