如何正确使用 useEffect 与 useContext 作为依赖
Posted
技术标签:
【中文标题】如何正确使用 useEffect 与 useContext 作为依赖【英文标题】:How to use useEffect correctly with useContext as a dependency 【发布时间】:2021-11-16 06:11:16 【问题描述】:我正在处理我的第一个 React 项目,但遇到以下问题。 我希望我的代码如何工作:
我将项目添加到可通过上下文访问的数组 (context.items) 我想在组件中运行 useEffect 函数,每当值发生变化时都会显示 context.items我尝试了什么:
-
将上下文(
context
和 context.items
)列为 useEffect 中的依赖项
-
列出
context.items.length
-
将上下文包装在
Object.values(context)
你知道有什么方法可以修复这个 React 警告,或者在上下文值更改时运行 useEffect 的不同方法吗?
好吧,我不想添加代码,希望这会是我这边的一些简单错误,但即使有一些答案,我仍然无法解决这个问题,所以在这里,为了简化而减少。
上下文组件:
const NewOrder = createContext(
orderItems: [
itemId: "",
name: "",
amount: 0,
more:[""]
],
addOrderItem: (newOItem: OrderItem) => ,
removeOrderItem: (oItemId: string) => ,
removeAllOrderItems: () => ,
);
export const NewOrderProvider: React.FC = (props) =>
// state
const [orderList, setOrderList] = useState<OrderItem[]>([]);
const context =
orderItems: orderList,
addOrderItem: addOItemHandler,
removeOrderItem: removeOItemHandler,
removeAllOrderItems: removeAllOItemsHandler,
;
// handlers
function addOItemHandler(newOItem: OrderItem)
setOrderList((prevOrderList: OrderItem[]) =>
prevOrderList.unshift(newOItem);
return prevOrderList;
);
function removeOItemHandler(oItemId: string)
setOrderList((prevOrderList: OrderItem[]) =>
const itemToDeleteIndex = prevOrderList.findIndex((item: OrderItem) => item.itemId === oItemId);
console.log(itemToDeleteIndex);
prevOrderList.splice(itemToDeleteIndex, 1);
return prevOrderList;
);
function removeAllOItemsHandler()
setOrderList([]);
return <NewOrder.Provider value=context>props.children</NewOrder.Provider>;
;
export default NewOrder;
显示数据的组件(实际上是一个模态):
const OrderMenu: React.FC< isOpen: boolean; hideModal: Function > = (
props
) =>
const NewOrderContext = useContext(NewOrder);
useEffect(() =>
if (NewOrderContext.orderItems.length > 0)
const oItems: JSX.Element[] = [];
NewOrderContext.orderItems.forEach((item) =>
const fullItem =
itemId:item.itemId,
name: item.name,
amount: item.amount,
more: item.more,
;
oItems.push(
<OItem item=fullItem editItem=() => editItem(item.itemId) key=item.itemId />
);
);
setContent(<div>oItems</div>);
else
exit();
, [NewOrderContext.orderItems.length, props.isOpen]);
代码中的一些 cmets:
它实际上是在 Type Script 中完成的,这涉及到一些额外的语法 -content(和设置内容)是一种状态,它是返回值的一部分,因此可以动态设置某些部分 -exit 是一个关闭模态的函数,也是为什么 props.is Open 被包含进来的原因 有了这个.length
扩展,当我从列表中删除一个项目时,模式显示会发生变化,但是,当我修改它时不会改变 orderItems 的长度,而只是其中一个对象的值。
正如我之前提到的,我找到了一些答案,他们说我应该像这样设置依赖项:...Object.values(<contextVariable>)
在技术上有效,但导致反应抱怨 *传递给 useEffect 的最终参数改变了渲染之间的大小。此数组的顺序和大小必须保持不变。 *
当我关闭并重新打开模式时,显示的值会更改为正确的值,更改 props.isOpen
表示问题出在上下文依赖中
【问题讨论】:
通过提供这段代码,我相信您会获得更多帮助。 如果对项目的引用没有改变,useEffect 将不会再次运行。这完全取决于你如何更新项目(你不应该改变状态)。 能否告诉我们 useEffect 包含什么以及如何更新状态(context.items)? @jperl 这是我认为问题所在代码的核心部分,希望我没有错过任何重要的内容 我不知道您在哪里找到这些告诉您使用...Object.values(<contextVariable>)
的答案,但请不要这样做,这太可怕了。 @EmmaJoe 实际上为您提供了一个很好的例子。你注意到区别了吗?正如我告诉你的,只要引用没有改变,useEffect 就不会重新运行。 EmmaJoe 解构了先前的值以构建一个新值,例如 let updatedCart = [...cart]
。这样我们就得到了一个新的参考。你没有,你取了以前的值并对其应用了 unshift 。这很糟糕,您不仅改变了状态,而且还没有获得新的引用。
【参考方案1】:
您可以先创建您的应用上下文,如下所示,我将使用购物车的示例
import * as React from "react"
const AppContext = React.createContext(
cart:[]
);
const AppContextProvider = (props) =>
const [cart,setCart] = React.useState([])
const addCartItem = (newItem)=>
let updatedCart = [...cart];
updatedCart.push(newItem)
setCart(updatedCart)
return <AppContext.Provider value=
cart
>props.children</AppContext.Provider>;
;
const useAppContext = () => React.useContext(AppContext);
export AppContextProvider, useAppContext ;
然后,您可以在应用程序的任何位置使用应用程序上下文,如下所示,只要购物车的长度发生变化,您就会在购物车中收到通知
import * as React from "react";
import useAppContext from "../../context/app,context";
const ShoppingCart: React.FC = () =>
const appContext = useAppContext();
React.useEffect(() =>
console.log(appContext.cart.length);
, [appContext.cart]);
return <div>appContext.cart.length</div>;
;
export default ShoppingCart;
【讨论】:
谢谢你的回答,虽然一开始我不明白为什么,这是正确的解决方案。 对于像我这样在查看此代码后仍然不明白的人,核心部分在问题下的 cmets 中进行了解释。【参考方案2】:您可以尝试将上下文变量传递给 useEffect 依赖数组,并在 useEffect 主体内部执行检查以查看值是否不为空。
【讨论】:
以上是关于如何正确使用 useEffect 与 useContext 作为依赖的主要内容,如果未能解决你的问题,请参考以下文章
如何在数组依赖中正确使用 useEffect 挂钩。我从 redux 商店传递了状态,但我的组件仍然无限渲染
如何使用 React Hooks 和 Context API 正确地将来自 useEffect 内部调用的多个端点的数据添加到状态对象?
如何正确地将事件侦听器添加到 React useEffect 挂钩?