React使用useEffect实现过滤器互斥时的状态更新不同步问题
问题原因
React 同一渲染周期内的状态更新是异步批量执行的,两个 useEffect 都监听了同一个specificPerson依赖,因此当specificPerson变更时,两个 useEffect 会同时触发。第一个 useEffect 中修改的hairColor等筛选状态要等到下一次渲染才会生效,第二个 useEffect 执行时读取到的还是旧的筛选值,导致逻辑判断错误。同时你将selectedScholars加入了第二个 useEffect 的依赖项,而 Effect 内部又会修改selectedScholars,会触发无限重渲染。
最优修复方案
selectedScholars属于完全由其他状态派生的结果,不需要单独声明为 state,直接使用useMemo计算即可,从根源上避免状态更新时序问题和无限循环问题:
import { useState, useMemo } from 'react'; // 原有状态保留 const [specificPerson, setSpecificPerson] = useState(false); const [hairColor, setHairColor] = useState(false); const [eyeColor, setEyeColor] = useState(false); const [gender, setGender] = useState(false); const allScholars = [/* 所有学生数组 */]; // 直接计算筛选结果 const selectedScholars = useMemo(() => { // specificPerson 优先级最高,只要有值直接返回,不用判断其他条件 if (specificPerson) { return [specificPerson]; } const list = []; if (hairColor) list.push(...allScholars.filter(byHairColor)); if (eyeColor) list.push(...allScholars.filter(byEyeColor)); if (gender) list.push(...allScholars.filter(byGender)); // 去重返回 return [...new Set(list)]; }, [allScholars, specificPerson, hairColor, eyeColor, gender]); // 原有清空筛选的useEffect可以保留,也可以把清空逻辑直接放在setSpecificPerson的调用位置,减少一次重渲染 useEffect(() => { setHairColor(false); setEyeColor(false); setGender(false); }, [specificPerson]); // 渲染直接使用selectedScholars即可
其他可选修复方案
如果你需要保留selectedScholars状态的话,可以调整第二个useEffect的判断逻辑,优先校验specificPerson是否存在,避开状态时序问题:
useEffect(() => { let list = []; // 优先判断specificPerson,优先级最高 if (specificPerson) { list = [specificPerson]; } else if (hairColor || eyeColor || gender) { if (hairColor) list.push(...allScholars.filter(byHairColor)); if (eyeColor) list.push(...allScholars.filter(byEyeColor)); if (gender) list.push(...allScholars.filter(byGender)); list = [...new Set(list)]; } else { // 无筛选条件时可按需返回全部学生或者空数组 list = allScholars; } setSelectedScholars(list); // 移除selectedScholars依赖项,避免无限循环 }, [allScholars, specificPerson, hairColor, eyeColor, gender, setSelectedScholars]);
内容的提问来源于stack exchange,提问作者Paolo Di Pietro
相关产品推荐
相关产品推荐

