React Context出现最大更新深度超限问题,求解决方案
这问题我之前也碰到过,核心原因就是你触发了无限渲染循环,咱们一步步理清楚怎么解决:
问题根源
你在Mats组件的渲染过程中直接调用了getProductCategory("mats"),而这个函数里又调用了setProductCategory——这个setState会触发ProductContextProvider的状态更新,进而导致所有消费这个Context的组件(包括Mats)重新渲染。重新渲染时又会再次调用getProductCategory,再次触发setState,循环就这么形成了,直到React触发最大更新深度的报错。
方案一:移除不必要的Context状态(推荐)
其实你根本不需要在Context里维护productsCategory这个状态,因为Mats组件只是需要过滤后的结果,没必要把它存在Context的状态中。直接修改Context里的getProductCategory函数,让它直接返回过滤好的数据就行:
修改后的ProductContextProvider代码:
import React, { createContext, useState, useEffect } from "react"; import data from "./data"; export const ProductContext = createContext(); const ProductContextProvider = ({ children }) => { const [products, setProducts] = useState(); // 给useEffect加空依赖数组,只执行一次初始化 useEffect(() => { setProducts(data); }, []); function getProductCategory(category) { // 直接返回过滤后的结果,无需触发状态更新 return data.filter((product) => product.type === category); } const getProduct = (product) => { const currentProduct = products ? products.filter((item) => item.slug === product)[0] : undefined; return currentProduct; }; return ( <ProductContext.Provider value={{ getProduct, getProductCategory }} > {children} </ProductContext.Provider> ); }; export default ProductContextProvider;
Mats组件不需要修改,这样调用getProductCategory("mats")就只是一个纯函数调用,不会触发状态更新,自然就不会有循环了。
方案二:如果必须维护Context状态,用useEffect触发更新
如果你确实需要在Context里保留productsCategory的状态(比如其他组件也要复用这个数据),那就在Mats组件里用useEffect来调用getProductCategory,只在组件挂载时执行一次:
修改后的Mats组件:
import React, { useContext, useEffect, useState } from "react"; import { ProductContext } from "../../context"; import CategoryTitle from "./accesoriesComponent/CategoryTitle/CategoryTitle"; // 顺便修正你之前写错的import路径 import Filters from "../../components/accesoriesComponent/Filters/Filters"; import ProductWrapper from "../../components/accesoriesComponent/ProductWrapper/ProductWrapper"; export default function Mats(props) { const { getProductCategory } = useContext(ProductContext); const [mats, setMats] = useState([]); useEffect(() => { // 只在组件挂载时调用一次 const categoryProducts = getProductCategory("mats"); setMats(categoryProducts); }, [getProductCategory]); // 加上依赖,确保函数变化时重新执行 return ( <div> <div style={{ display: "flex" }}> <Filters /> {mats.length > 0 && <ProductWrapper products={mats} />} </div> </div> ); }
同时,Context里的getProductCategory要调整一下,直接返回刚过滤的结果(因为setState是异步的,原来返回的productsCategory可能是旧值):
function getProductCategory(category) { const productFromCategory = data.filter( (product) => product.type === category ); setProductCategory(productFromCategory); console.log(productFromCategory); return productFromCategory; // 返回最新过滤结果,而非状态中的值 }
另外还要提醒你:Provider里的useEffect原本没有依赖数组,会每次渲染都执行setProducts(data),虽然data是静态的,但最好加上空数组[],避免不必要的重复执行,我在方案一的代码里已经做了这个修正。
内容的提问来源于stack exchange,提问作者Sebastian

