React useEffect执行后filteredProject值正确但页面不更新问题
问题根因
你的判断完全准确,页面不更新的核心原因就是filteredProject不是响应式状态:
- 你在组件顶层用
let filteredProject = projectsList声明的是普通临时变量,每次组件重渲染时,这个变量都会被重置为原始的projectsList值。你在useEffect里对这个普通变量的赋值,既不会触发React的重渲染流程,就算触发了重渲染,修改后的值也会被顶层的重新声明覆盖。 - 代码逻辑本身也存在不一致:判断筛选值为
All的分支里你调用了未声明的setfilteredProject方法,其余分支又直接给普通变量赋值,本身逻辑就没有闭环。 - 额外的小问题:你给筛选状态
state设置的初始值是数字0,但所有单选按钮的value都是字符串类型,首次渲染时过滤逻辑会出现类型不匹配的问题。
修正方案
这类基于现有状态做派生计算的场景,完全不需要使用useEffect,直接在渲染阶段计算过滤结果即可,逻辑更简洁也不会出现状态同步问题,参考代码如下:
// 修正初始值类型,和单选框value保持一致,变量名避免和redux的state重名减少混淆 const [statusFilter, setStatusFilter] = useState("All") const projectsList = useSelector(state=>state.projectReducer.projects) // 用useMemo缓存过滤结果,仅在依赖项变化时重新计算,避免不必要的性能消耗 const filteredProject = useMemo(() => { if (!projectsList) return [] if (statusFilter === "All") return projectsList return projectsList.filter(item => item.state === statusFilter) }, [projectsList, statusFilter]) function toggleStatus(e){ setStatusFilter(e.target.value) } return ( <div className="px-2 py-4 "> <div className="card h-fit my-4 lg:w-12/12"> <div className='form-check form-check-inline'> <input className='form-check-input appearance-none h-4 w-4 border border-gray-300 rounded-sm bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer' name='status' type='radio' onChange={toggleStatus} value='All' checked={statusFilter === 'All'} /> <label className='form-check-label inline-block text-gray-800 font-normal mr-2'><b>All Statuses</b></label> </div> <div className='form-check form-check-inline'> <input className='form-check-input appearance-none h-4 w-4 border border-gray-300 rounded-sm bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer' name='status' type='radio' onChange={toggleStatus} value='Fundraising' checked={statusFilter === 'Fundraising'} /> <label className='form-check-label inline-block text-gray-800 font-normal mr-2'>Fundraising</label> </div> <div className='form-check form-check-inline'> <input className='form-check-input appearance-none h-4 w-4 border border-gray-300 rounded-sm bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer' name='status' type='radio' onChange={toggleStatus} value='Successful' checked={statusFilter === 'Successful'} /> <label className='form-check-label inline-block text-gray-800 font-normal mr-2'>Successful</label> </div> <div className='form-check form-check-inline'> <input className='form-check-input appearance-none h-4 w-4 border border-gray-300 rounded-sm bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer' name='status' type='radio' onChange={toggleStatus} value='Expired' checked={statusFilter === 'Expired'} /> <label className='form-check-label inline-block text-gray-800 font-normal mr-2'>Expired</label> </div> </div> <div className="my-2 lg:my-0 lg:mx-2 grid sm:grid-cols-3 sm:gap-2 grid-cols-1"> {filteredProject.map(/* 原有列表渲染逻辑 */)} </div> </div> )
补充说明
- 单选控件建议用
onChange替代onClick,更符合表单交互的规范;同时用checked属性受控绑定状态值,替代defaultChecked的非受控写法,避免选中状态和实际变量值不同步。 - 如果你坚持要用
useEffect实现,必须先把filteredProject声明为响应式状态:const [filteredProject, setFilteredProject] = useState([]),且所有分支都通过setFilteredProject更新值,不能直接给变量赋值。但这种写法会多触发一次组件重渲染,性能不如直接派生计算的方案。
内容的提问来源于stack exchange,提问作者Christopher Stobbs
相关产品推荐
相关产品推荐

