React useEffect非预期触发 实现按钮点击搜索的解决方案
问题根因
异常触发本质是逻辑设计和依赖规则冲突导致的:
- 没有拆分「输入框临时输入值」和「实际生效的搜索参数」,输入过程中直接修改被请求逻辑引用的
search状态,加上你用// eslint-disable-next-line屏蔽了Hooks依赖校验,导致依赖数组声明和实际用到的响应式值不一致。你虽然没把search加入依赖数组,但每次输入触发组件重渲染后,只要limitPage/page/products任意一个依赖项引用变化,useEffect执行时就会通过闭包拿到最新的search值发请求,造成“输入就触发搜索”的假象。 - 你在
handleSearch里手动调用getProducts,和useEffect里的请求逻辑重复,同时React的setState是异步批量更新的,调用完setLimitPage/setPage后立刻取状态值会拿到旧值,导致请求参数错误。
修复方案
第一步:拆分两个独立状态
把输入框的临时输入值和实际提交后才生效的搜索词分开,输入过程中不会修改任何useEffect依赖的变量,从根源避免输入时触发副作用:
// 输入框实时绑定的临时值,输入时修改不会触发搜索请求 const [searchInput, setSearchInput] = useState(""); // 点击搜索按钮后才更新的生效搜索参数,仅这个值会参与请求逻辑 const [activeSearch, setActiveSearch] = useState(""); // 其余分页状态保留 const [limitPage, setLimitPage] = useState(10); const [page, setPage] = useState(1); // ... 已有的numbers、totalDocs、products等状态
第二步:修正useEffect逻辑,移除依赖屏蔽
不要用eslint注释绕开依赖检查,把所有副作用里用到的响应式值都加入依赖数组,保证依赖关系和实际执行逻辑一致,同时修复原代码里分页计算取值的bug:
useEffect(() => { // 统一在这里处理分页、搜索的产品请求 getProducts(limitPage, page, activeSearch); getXlsProducts(100000, 1); // 等products数据返回后再计算分页信息 if (products?.products) { const totalDocs = products.products.totalDocs; const totalPages = Math.ceil(totalDocs / limitPage); const links = []; for (let i = 1; i <= totalPages; i++) { links[i] = i; } setNumbers(links); setTotalDocs(totalDocs); setTotalPages(products.products.totalPages); setCurrentPage(products.products.page); setNextPage(products.products.hasNextPage); setPreviousPage(products.products.hasPrevPage); } // 所有用到的响应式值全加入依赖,不要加eslint屏蔽 }, [limitPage, page, activeSearch, products]);
第三步:修正输入框和搜索按钮逻辑
输入框只绑定临时的searchInput,点击按钮时才把临时值同步到activeSearch,同时重置分页参数,不需要手动调用getProducts——只要依赖项变化,useEffect会自动执行请求:
<div> <input className="border-1 border-2 border-solid rounded border-slate-300" value={searchInput} onChange={(e) => setSearchInput(e.target.value)} /> <button onClick={() => { // 点击按钮才更新生效的搜索词,同时重置分页 setActiveSearch(searchInput); setLimitPage(10); setPage(1); }}> <FiSearch /> </button> </div>
额外注意点
- 之前独立定义的
pages函数可以删掉,所有逻辑收敛到useEffect里即可,避免闭包引用旧状态的问题。 - 永远不要用
// eslint-disable-next-line屏蔽Hooks的依赖校验,绝大多数React useEffect非预期触发问题,都是因为屏蔽校验导致依赖声明和实际逻辑不一致。 - 不要在useEffect和点击事件里重复写请求逻辑,所有依赖参数变化触发的请求统一交给useEffect处理,事件里只负责更新状态参数即可。
内容的提问来源于stack exchange,提问作者OscarDev
相关产品推荐
相关产品推荐

