問題描述
更新 useContext 中的值時,組件不會重新呈現 (Component not re rendering when value from useContext is updated)
我正在使用 React 的上下文 api 來存儲項目數組。有一個組件可以通過 useContext() 訪問這個數組並顯示數組的長度。還有另一個組件可以訪問該函數以通過 useContext 更新此數組。將項目添加到數組時,組件不會重新渲染以反映數組的新長度。當我導航到應用程序中的另一個頁面時,組件會重新呈現並反映數組的當前長度。每當上下文中的數組發生變化時,我都需要重新渲染組件。
我嘗試使用 Context.Consumer 而不是 useContext,但是當數組發生變化時它仍然不會重新渲染。
//orderContext.js//
import React, { createContext, useState } from "react"
const OrderContext = createContext({
addToOrder: () => {},
products: [],
})
const OrderProvider = ({ children }) => {
const [products, setProducts] = useState([])
const addToOrder = (idToAdd, quantityToAdd = 1) => {
let newProducts = products
newProducts[newProducts.length] = {
id: idToAdd,
quantity: quantityToAdd,
}
setProducts(newProducts)
}
return (
<OrderContext.Provider
value={{
addToOrder,
products,
}}
>
{children}
</OrderContext.Provider>
)
}
export default OrderContext
export { OrderProvider }
//addToCartButton.js//
import React, { useContext } from "react"
import OrderContext from "../../../context/orderContext"
export default ({ price, productId }) => {
const { addToOrder } = useContext(OrderContext)
return (
<button onClick={() => addToOrder(productId, 1)}>
<span>${price}</span>
</button>
)
}
//cart.js//
import React, { useContext, useState, useEffect } from "react"
import OrderContext from "../../context/orderContext"
export default () => {
const { products } = useContext(OrderContext)
return <span>{products.length}</span>
}
//gatsby‑browser.js//
import React from "react"
import { OrderProvider } from "./src/context/orderContext"
export const wrapRootElement = ({ element }) => (
<OrderProvider>{element}</OrderProvider>
)
我希望購物車組件會在更新數組時顯示數組的新長度,但在我導航到另一個頁面時重新渲染組件之前它保持不變。每次更新上下文中的數組時,我都需要它重新渲染。
參考解法
方法 1:
The issue is likely that you're mutating the array (rather than setting a new array) so React sees the array as the same using shallow equality.
Changing your addOrder
method to assign a new array should fix this issue:
const addToOrder = (idToAdd, quantityToAdd = 1) =>
setProducts([
...products,
{
id: idToAdd,
quantity: quantityToAdd
}
]);
方法 2:
As @skovy said, there are more elegant solutions based on his answer if you want to change the original array.
setProducts(prevState => {
prevState[0].id = newId
return [...prevState]
})
方法 3:
<p>@skovy's description helped me understand why my component was not re‑rendering.</p>In my case I had a provider that held a large dictionary and everytime I updated that dictionary no re‑renders would happen.
ex:
const [var, setVar] = useState({some large dictionary});
...mutate same dictionary
setVar(var) //this would not cause re‑render
setVar({...var}) // this caused a re‑render because it is a new object
I would be weary about doing this on large applications because the re‑renders will cause major performance issues. in my case it is a small two page applet so some wasteful re‑renders are ok for me.
(by Jordan Paz、skovy、mutoe、Cory Lewis)