reactjs - 如何以 useContext 作为依赖项正确使用 useEffect

标签 reactjs use-effect react-functional-component use-context

我正在开发我的第一个 React 项目,但遇到以下问题。 我希望我的代码如何工作:

  • 我将项目添加到可通过上下文访问的数组中 (context.items)
  • 我想在组件中运行 useEffect 函数,只要值发生变化,就会显示 context.items

我尝试过的:

  1. 将上下文( contextcontext.items )列为 useEffect 中的依赖项
  • 这导致组件在值更改时不更新
  • 列出context.items.length
    • 这会导致组件在数组长度发生变化时更新,而不是在单个项目的值发生变化时更新。
  • 将上下文包装在 Object.values(context)
    • 结果正是我想要的,除了 React 现在提示 *传递给 useEffect 的最后一个参数改变了渲染之间的大小。该数组的顺序和大小必须保持不变。 *

    您知道有什么方法可以修复此 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]);
    

    对代码的一些注释:

    • 它实际上是用 Type Script 完成的,涉及一些额外的语法 -content(和设置内容)是一种状态,它是返回值的一部分,因此可以动态设置某些部分 -exit 是关闭模式的函数,也是包含 props.is Open 的原因
    • 有了这个.length 扩展当我从列表中删除一个项目时,模式会显示更改,但是,当我修改它时,不会更改 orderItems 的长度,而只会更改其中一个对象的值。
    • 正如我之前提到的,我找到了一些答案,他们说我应该像这样设置依赖项:...Object.values(<contextVariable>)这在技术上是可行的,但会导致 react 提示*传递给 useEffect 的最后一个参数改变了渲染之间的大小。该数组的顺序和大小必须保持不变。 *
    • 当我关闭并重新打开模式时,显示的值更改为正确的值,更改 props.isOpen说明问题出在上下文依赖上

    最佳答案

    您可以首先创建应用程序上下文,如下所示,我将使用购物车的示例

    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;
    

    关于reactjs - 如何以 useContext 作为依赖项正确使用 useEffect,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69289935/

    相关文章:

    javascript - 为什么我无法运行我的 React Native 项目?

    asp.net - 为什么将 ReactJS 与 ASP.NET CORE MVC 结合使用?

    javascript - 使用 Javascript 或 Typescript 编写 React.js 应用程序

    reactjs - 带有 useEffect 的自定义钩子(Hook)(在挂载上)执行了多次

    reactjs - useEffect() 导致重新渲染和对 api 的多次请求

    javascript - 如何阻止多个重新渲染执行多个 api 调用 useEffect?

    javascript - 如何将 Chosen 合并到我的 React 项目中?

    javascript - React,在渲染组件之前从数据库获取数据

    reactjs - 使用 React Context 中的使用效果检查本地存储中是否已有 token 的最佳方法

    reactjs - 更新的状态值不会在 react 中的函数内部更新