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;
- 你写错了上下文。要设置新的 foodState 值,您不能使用 this.foodState,但您需要使用 useState 挂钩来保存和更新该值。
- 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;
我是经验丰富的 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;
- 你写错了上下文。要设置新的 foodState 值,您不能使用 this.foodState,但您需要使用 useState 挂钩来保存和更新该值。
- 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;