从子组件更新上下文时,我们可以触发父组件渲染吗?
Can we trigger parent component render when Context is updated from child component?
我有一个子组件,它是一个产品项。我在其中使用上下文来修改 json。当我修改项目的数量时(我现在使用 json 作为假数据库)。
在父组件中,我需要更新新的总数量、价格等。所以我在这个组件中订阅了 useEffect() 以进行数据更改,但它不会触发。我做错了什么?
cartAPIContext.js :
import { createContext, useEffect, useContext, useState, useMemo } from "react";
export const CartAPIContext = createContext();
const CartAPIContextProvider = ({ children }) => {
const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
//On ira chercher le dernier panier actif de l'utilisateur
let url = "/cartFakedb.json";
fetch(url)
.then(response => response.json())
.then((data) => {
setData(data);
setIsLoading(false);
})
.catch((error) => console.log(error));
}, [])
const contextValue = useMemo(() => ({
data,
setData,
isLoading
}), [data, setData, isLoading])
return (
<CartAPIContext.Provider value={contextValue}>
{children}
</CartAPIContext.Provider>
)
}
export default CartAPIContextProvider;
export function useCartAPI() {
const context = useContext(CartAPIContext);
if (context === undefined) {
throw new Error("Context must be used within a Provider");
}
return context;
}
子组件:
import { forwardRef, useContext, useEffect, useState } from "react";
import { useCartAPI } from "./CartAPIContext";
const CartProductItem = ({ libraryId, product }) => {
const { data, setData } = useCartAPI();
const [ selectedItemQuantity, setSelectedItemQuantity ] = useState(product.quantity);
const [ itemInStock, setItemInStock ] = useState(product.stock);
const [ itemPrice, setItemPrice ] = useState((product.unitPrice*selectedItemQuantity).toFixed(2));
function changeProductQuantity(event) {
setSelectedItemQuantity(event.target.value);
}
useEffect(() => {
for(let i = 0; i < data.libraries.length; i++) {
if(data.libraries[i].id === libraryId) {
for(let j = 0; j < data.libraries[i].products.length; j++) {
if(data.libraries[i].products[j].id === product.id) {
data.libraries[i].products[j].quantity = parseInt(selectedItemQuantity);
}
}
}
}
setData(data);
setItemPrice((product.unitPrice*selectedItemQuantity).toFixed(2));
}, [selectedItemQuantity]);
console.log(data);
return (
<li className="item">
<article>
<div className="left">
<img src={product.cover_img} alt={product.title} loading="lazy" />
<select value={selectedItemQuantity} onChange={changeProductQuantity}>
{Array.from(Array(itemInStock)).map((empty, index) =>
<option key={product.id + index} value={index + 1}>{index + 1}</option>
)}
</select>
</div>
</article>
</li>
);
}
export default CartProductItem;
父组件:
import { useCartAPI } from "./CartAPIContext";
import CartProductList from "./CartProductList";
import { useEffect } from "react";
const CartLibraryItem = ({ library }) => {
const { data, setData } = useCartAPI();
function getTotalNumberOfArticles() {
let total = {quantity: library.products[0].quantity};
if (library.products.length > 1) {
total = library.products.reduce((productA, productB) => {
return { quantity: productA.quantity + productB.quantity };
})
}
console.log(total);
return total;
}
useEffect(() => {
getTotalNumberOfArticles();
}, [data]) //<---- I'm looking to trigger this useEffect(), when I use the select in the child component above.
return (
<li className="cart-library-item" key={library.id}>
//I delete this part for space
</li>
);
}
export default CartLibraryItem;
data.libraries[i].products[j].quantity = parseInt(selectedItemQuantity);
//...
setData(data);
您正在改变状态,然后使用状态中已有的同一对象设置状态。 React 将旧状态与新状态进行比较,发现它们是同一个对象,因此它不会渲染。
您需要创建一个新状态。对于这样一个深度嵌套的对象,这有点痛苦,但它会是这样的:
useEffect(() => {
const newData = {
...data,
libraries: data.libraries.map(library => {
if (library.id !== libraryId) {
return library;
}
return {
...library,
products: library.products.map(p => {
if (p.id !== product.id) {
return p;
}
return {
...p,
quantity: parseInt(selectedItemQuantity)
}
});
}
})
}
setData(newData);
setItemPrice((product.unitPrice*selectedItemQuantity).toFixed(2));
}, [selectedItemQuantity]);
我有一个子组件,它是一个产品项。我在其中使用上下文来修改 json。当我修改项目的数量时(我现在使用 json 作为假数据库)。 在父组件中,我需要更新新的总数量、价格等。所以我在这个组件中订阅了 useEffect() 以进行数据更改,但它不会触发。我做错了什么?
cartAPIContext.js :
import { createContext, useEffect, useContext, useState, useMemo } from "react";
export const CartAPIContext = createContext();
const CartAPIContextProvider = ({ children }) => {
const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
//On ira chercher le dernier panier actif de l'utilisateur
let url = "/cartFakedb.json";
fetch(url)
.then(response => response.json())
.then((data) => {
setData(data);
setIsLoading(false);
})
.catch((error) => console.log(error));
}, [])
const contextValue = useMemo(() => ({
data,
setData,
isLoading
}), [data, setData, isLoading])
return (
<CartAPIContext.Provider value={contextValue}>
{children}
</CartAPIContext.Provider>
)
}
export default CartAPIContextProvider;
export function useCartAPI() {
const context = useContext(CartAPIContext);
if (context === undefined) {
throw new Error("Context must be used within a Provider");
}
return context;
}
子组件:
import { forwardRef, useContext, useEffect, useState } from "react";
import { useCartAPI } from "./CartAPIContext";
const CartProductItem = ({ libraryId, product }) => {
const { data, setData } = useCartAPI();
const [ selectedItemQuantity, setSelectedItemQuantity ] = useState(product.quantity);
const [ itemInStock, setItemInStock ] = useState(product.stock);
const [ itemPrice, setItemPrice ] = useState((product.unitPrice*selectedItemQuantity).toFixed(2));
function changeProductQuantity(event) {
setSelectedItemQuantity(event.target.value);
}
useEffect(() => {
for(let i = 0; i < data.libraries.length; i++) {
if(data.libraries[i].id === libraryId) {
for(let j = 0; j < data.libraries[i].products.length; j++) {
if(data.libraries[i].products[j].id === product.id) {
data.libraries[i].products[j].quantity = parseInt(selectedItemQuantity);
}
}
}
}
setData(data);
setItemPrice((product.unitPrice*selectedItemQuantity).toFixed(2));
}, [selectedItemQuantity]);
console.log(data);
return (
<li className="item">
<article>
<div className="left">
<img src={product.cover_img} alt={product.title} loading="lazy" />
<select value={selectedItemQuantity} onChange={changeProductQuantity}>
{Array.from(Array(itemInStock)).map((empty, index) =>
<option key={product.id + index} value={index + 1}>{index + 1}</option>
)}
</select>
</div>
</article>
</li>
);
}
export default CartProductItem;
父组件:
import { useCartAPI } from "./CartAPIContext";
import CartProductList from "./CartProductList";
import { useEffect } from "react";
const CartLibraryItem = ({ library }) => {
const { data, setData } = useCartAPI();
function getTotalNumberOfArticles() {
let total = {quantity: library.products[0].quantity};
if (library.products.length > 1) {
total = library.products.reduce((productA, productB) => {
return { quantity: productA.quantity + productB.quantity };
})
}
console.log(total);
return total;
}
useEffect(() => {
getTotalNumberOfArticles();
}, [data]) //<---- I'm looking to trigger this useEffect(), when I use the select in the child component above.
return (
<li className="cart-library-item" key={library.id}>
//I delete this part for space
</li>
);
}
export default CartLibraryItem;
data.libraries[i].products[j].quantity = parseInt(selectedItemQuantity);
//...
setData(data);
您正在改变状态,然后使用状态中已有的同一对象设置状态。 React 将旧状态与新状态进行比较,发现它们是同一个对象,因此它不会渲染。
您需要创建一个新状态。对于这样一个深度嵌套的对象,这有点痛苦,但它会是这样的:
useEffect(() => {
const newData = {
...data,
libraries: data.libraries.map(library => {
if (library.id !== libraryId) {
return library;
}
return {
...library,
products: library.products.map(p => {
if (p.id !== product.id) {
return p;
}
return {
...p,
quantity: parseInt(selectedItemQuantity)
}
});
}
})
}
setData(newData);
setItemPrice((product.unitPrice*selectedItemQuantity).toFixed(2));
}, [selectedItemQuantity]);