UseContext 不会重新执行组件

UseContext doesn't re-execute component

我是经验丰富的 javascript 开发人员。但是我必须继续做出反应,所以现在我知道的概念很少。我已经调试这段代码 4 个小时了,没有发现任何错误。我的意思是,这可能是因为这个钩子有点混乱。

应用程序应该做什么?基本上它应该根据虚拟编码数据显示食物成分。它运作良好。在那个新显示的组件上应该是一个具有输入字段的表单。当我们提交表格时,表格中的数字应该被获取并写在一个食物对象中。对象应如下所示:{Sushi: 0}。每当我提交时,它都应该更新。除此之外,对象中不只有一个属性。可能还有更多,例如:{Sushi: 0, Pizza: 0, Cherry: 0}。这取决于虚拟数据。在应用程序开始时,它会自动创建此对象并推入状态。然后,我将状态更新函数发送到子组件,当我们提交表单时,它更新了状态。那很好用。现在在当前组件上,我有 useEffect。每当状态发生变化时,使用效果就会触发。它的作用是更新上下文。现在,当我更新上下文时,在“第三”组件中,应该采用所有值,并且应该显示值的总和。问题是,无论我如何尝试,它都不会重新执行“第三”组件...

代码: App.js

function App() {
return (
<FoodStateContext.Provider
  value={{
    foodState: {},
    setContext: function (val) {
      this.foodState = val;
    },
  }}
>
  <Header />
  <BodyContainer>
    <Card className={styles["food-message"]}>
      <h2>Delicious Food, Delivered To You</h2>
      <p>
        Choose your favourite meal from our broad selection of avaible meets
        and enjoy a delicious lunch or dinner at home.
      </p>
      <p>
        All our meals are cooked with high-quality ingredients, just-in-time
        and of course by experienced chefs!
      </p>
    </Card>
    <FoodContainer foodList={food} />
  </BodyContainer>
</FoodStateContext.Provider>
);
}

导出默认应用;


食品容器

import react, { useState, useEffect, useReducer, useContext } from "react";

import styles from "./FoodContainer.module.css";
import Card from "../UI/Card/Card";
import FoodComponent from "./FoodComponent";
import FoodStateContext from "../store/food-state-context";

const FoodContainer = (props) => {
const foodList = props.foodList;

const [foodState, setFoodState] = useState({});
const ctx = useContext(FoodStateContext);

useEffect(() => {
let obj = {};
for (let i = 0; i < foodList.length; i++) {
  obj[foodList[i].name] = 0;
}
setFoodState(obj);
}, []);

console.log(foodState);

useEffect(() => {
console.log("muda");
ctx.foodState = foodState;
ctx.setContext(foodState);
console.log(ctx.foodState);
}, [foodState]);

return (
<Card className={styles["food-container"]}>
  {foodList.map((food) => {
    return (
      <FoodComponent
        key={food.name}
        food={food}
        updateFoodState={setFoodState}
      ></FoodComponent>
    );
  })}
</Card>

); };

导出默认 FoodContainer;


食物成分

import React, { useReducer, useEffect, useContext } from "react";
import styles from "./FoodComponent.module.css";
import Input from "../UI/Input/Input";
import Button from "../UI/Button/Button";

const FoodComponent = (props) => {
const food = props.food;

const formSubmitHandler = (e) => {
e.preventDefault();
props.updateFoodState((prevState) => {
  return {
    ...prevState,
    [food.name]: prevState[food.name] + +e.target[0].value,
  };
});
};

return (
<div className={styles["food-component"]}>
  <div className={styles["food-component__description"]}>
    <p>{food.name}</p>
    <p>{food.description}</p>
    <p>{food.price}</p>
  </div>

  <form onSubmit={formSubmitHandler}>
    <div>
      <div>
        <span>Amount:</span>
        <Input />
      </div>
      <Button type="submit">+Add</Button>
    </div>
  </form>
</div>
);
};

export default FoodComponent;

食物背景

import react from "react";

const FoodStateContext = react.createContext({
foodState: "",
setContext: (val) => {
console.log(val);
this.foodState = val;
},
});

export default FoodStateContext;

第三个组成部分

import React, { useState, useContext, useEffect } from "react";
import styles from "./Cart.module.css";
import FoodStateContext from "../store/food-state-context";

const Cart = (props) => {
const [totalProductsNumber, setTotalProductsNumber] = useState(0);

const ctx = useContext(FoodStateContext);
console.log(ctx.foodState);
useEffect(() => console.log("govna"), [ctx]);

return (
<div className={styles.cart}>
  <div>Ic</div>
  <p>Your Cart</p>
  <div>
    <span>{totalProductsNumber}</span>
  </div>
</div>
);
};

export default Cart;
  1. 你写错了上下文。要设置新的 foodState 值,您不能使用 this.foodState,但您需要使用 useState 挂钩来保存和更新该值。
  2. React.useEffect 不对对象使用深度比较,因此永远不会触发第三个组件中的条件。

这是我的解决方案。我没有测试过,但我做了一些更改,应该可以解决您的问题并优化您的代码。

// FoodStateProvider.js
import React from "react";

export const FoodStateContext = React.useContext({
  foodState: {},
  setFoodState: () => undefined,
});

// Use a custom provider to handle the context logic
export const FoodStateProvider = ({ children }) => {
  const [foodState, setFoodState] = React.useState({});

  return (
    <FoodStateContext.Provider
      value={{
        foodState,
        setFoodState,
      }}
    >
      {children}
    </FoodStateContext.Provider>
  );
};

然后在您的应用中使用 FoodStateProvider

// App.js
const App = () => (
  <FoodStateProvider> ...your components </FoodStateProvider>
)

然后,我删除了 FoodContainer 中的 useState,因为现在您在上下文中处理它:

// FoodContainer.js
import React, { useState, useEffect, useContext } from "react";

import styles from "./FoodContainer.module.css";
import Card from "../UI/Card/Card";
import FoodComponent from "./FoodComponent";
import FoodStateContext from "../store/food-state-context";

const FoodContainer = ({ foodList }) => {
  const ctx = useContext(FoodStateContext);

  // The useEffect don't works well here, using a callback helps to remove 
  // additional data structures and conditions problems
  const setFoodState = useCallback(({name, valueToAdd}) => {
    // Logic from FoodComponent now is here
    const newValue = (ctx.foodState[name] ?? 0) + valueToAdd;
    ctx.setFoodState(currentFoodState => {...currentFoodState, [name]: newValue});
  }, [foodState]);

  return (
    <Card className={styles["food-container"]}>
      {foodList.map((food) => (
        <FoodComponent
          key={food.name}
          food={food}
          updateFoodState={setFoodState}
        />
      ))}
    </Card>
  );
};
// FoodComponent.js
import React, { useReducer, useEffect, useContext } from "react";
import styles from "./FoodComponent.module.css";
import Input from "../UI/Input/Input";
import Button from "../UI/Button/Button";

const FoodComponent = ({ food, updateFoodState }) => {
  const formSubmitHandler = (e) => {
    e.preventDefault();
    updateFoodState({ name: food.name, valueToAdd: e.target[0].value });
  };

  return (
    <div>
      your components
      <form onSubmit={formSubmitHandler}> your form </form>
    </div>
  );
};

export default FoodComponent;