实现产品实时搜索时useEffect触发Maximum depth exceeded报错如何解决
问题根因
你遇到的是典型的React状态更新死循环问题,核心原因有两个:
useEffect的依赖数组包含productList,同时useEffect回调内执行了setProductList更新该状态,状态更新会重新触发useEffect执行,形成无限递归- 你把全量原始产品数据和过滤后的结果存在同一个
productList状态里,不仅导致死循环,还会出现二次搜索无法匹配全量数据的逻辑问题:比如第一次搜「A」过滤出相关产品后,productList已经被覆盖为仅含A的列表,后续再搜「B」只会在A的结果中匹配,无法得到正确结果。
修复方案
拆分状态,将全量原始数据和过滤逻辑分离,不需要把过滤后的结果存为状态,直接基于原始数据和搜索关键字计算即可:
- 新增
originalProductList状态存储后端返回的全量产品数据 - 单独写
useEffect拉取全量数据,仅在组件挂载时执行一次(如果有增删改操作需要刷新数据,可额外添加对应的触发依赖) - 用
useMemo计算过滤后的产品列表,不需要在useEffect中修改状态触发重渲染
修正后代码示例
import { useState, useEffect, useMemo } from 'react'; import Axios from 'axios'; import { useHistory } from 'react-router-dom'; function filterProducts (productName, productList) { // 可根据需求调整匹配规则,示例改成了不区分大小写的模糊匹配,比原来的全等更符合搜索场景 const trimedName = productName.trim().toLowerCase(); return productList.filter((prod)=> { return prod.title.toLowerCase().includes(trimedName); }); } function HomePage () { const [originalProductList, setOriginalProductList] = useState([]); const [popupTrigger, setPopupTrigger] = useState(''); const [productDeleteId, setProductDeleteId] = useState(''); const [queryString, setQueryString] = useState(''); let history = useHistory(); // 仅拉取全量原始数据,不参与过滤逻辑 useEffect(() => { Axios.get("http://localhost:3001/api/product/get-all").then((data) => { setOriginalProductList(data.data); }); }, []); // 基于原始数据和搜索关键字实时计算过滤结果,不需要存状态 const filteredProductList = useMemo(() => { if (queryString.trim() === "") { return originalProductList; } return filterProducts(queryString, originalProductList); }, [queryString, originalProductList]); // 后续渲染产品列表直接用filteredProductList即可 // 其余原有逻辑保留... }
额外说明
如果你的场景要求每次清空搜索框都重新拉取最新的全量数据,可在上述代码基础上额外加一个单独的useEffect监听queryString,判断为空时调用接口更新originalProductList即可,不要把productList相关的状态加到依赖数组里,避免重复触发。
内容的提问来源于stack exchange,提问作者JoaoP_L
相关产品推荐
相关产品推荐

