React useState更新延迟点击生效问题及双useEffect合并诉求
问题分析与解决方案
核心问题原因
- 直接修改State对象:你在useEffect里直接修改
rowData的属性(如rowData[rowKey] = { ...rowData[rowKey], isHidden: false }),这属于篡改状态,React无法检测到对象内部的变化,导致组件不触发重新渲染,所以第一次点击下拉无反应,第二次点击时依赖项变化才会渲染,显示之前的操作结果。 - 筛选逻辑互相覆盖:两个useEffect分别处理分类和SDLR筛选,每次触发都会重置所有行的
isHidden,导致另一个筛选的结果被覆盖,比如选择People分类后,SDLR的useEffect会把所有行的isHidden重置为false,破坏分类筛选的结果。 - 未保存原始数据:每次筛选都是基于修改后的
rowData,导致原始数据被污染,无法正确恢复全量数据。
修复后的完整代码
import { React, useEffect, useState, useMemo } from "react"; import TableRow from "../../components/TableRow/TableRow"; import axios from "axios"; export default function SellerList() { // 保存原始接口数据,不做任何修改 const [originalRowData, setOriginalRowData] = useState({}); // 初始化筛选值为默认的"00",避免undefined const [categoryValue, setCategoryValue] = useState("00"); const [sdlrValue, setSdlrValue] = useState("00"); // 仅在组件挂载时获取数据 useEffect(() => { axios .get("SellerList.json") .then(function (response) { setOriginalRowData(response.data); }) .catch(function (error) { console.log(error); }); }, []); // 使用useMemo缓存筛选后的结果,避免每次渲染重复计算 const filteredRowData = useMemo(() => { // 先复制原始数据,避免修改原始对象 const data = { ...originalRowData }; Object.keys(data).forEach(rowKey => { const row = data[rowKey]; // 初始默认显示所有行 let isHidden = false; // 分类筛选逻辑 if (categoryValue !== "00" && row.code !== categoryValue) { isHidden = true; } // SDLR筛选逻辑:如果分类已经隐藏,就不用再判断SDLR;否则判断SDLR条件 if (!isHidden && sdlrValue !== "00" && row.sdlrId !== sdlrValue.trim()) { isHidden = true; } data[rowKey] = { ...row, isHidden }; }); return data; }, [originalRowData, categoryValue, sdlrValue]); return ( <div> <h1>Seller Deduplicated Request List</h1> <table> <thead> <tr> <th></th> <th>Code</th> <th>Category</th> <th>SDRL ID</th> <th>Data Requested (Name or Label for request)</th> <th>Request Parameters (Date ranges, etc)</th> <th>Request Details (Notes to explain request)</th> <th>Format (File type or format for request)</th> </tr> <tr> <th></th> <th colSpan={2}> <select value={categoryValue} onChange={(event) => setCategoryValue(event.target.value)} > <option value="00">Filter by Category</option> <option value="02">People</option> <option value="01">Finance & Accounting</option> <option value="04">Purchasing</option> <option value="03">Sales</option> {/* 修复重复value问题 */} </select> </th> <th> <select value={sdlrValue} onChange={(event) => setSdlrValue(event.target.value)} > <option value="00">Filter by SDLR</option> <option value="01.02">01.02</option> <option value="02.01">02.01</option> <option value="04.01">04.01</option> {/* 去除多余空格 */} <option value="05.01">05.01</option> </select> </th> <th></th> <th></th> <th></th> <th></th> <th></th> </tr> </thead> <tbody> {Object.keys(filteredRowData).map((rowKey) => ( <TableRow key={rowKey} row={filteredRowData[rowKey]} setRowData={setOriginalRowData} rowData={filteredRowData} /> ))} </tbody> </table> </div> ); }
关键修改点说明
- 分离原始数据与筛选后数据:新增
originalRowData存储接口返回的原始数据,筛选操作基于原始数据进行,避免数据污染。 - 使用useMemo合并筛选逻辑:将分类和SDLR的筛选逻辑合并在一处,仅在原始数据或筛选条件变化时重新计算,既解决了逻辑分散的问题,又避免了直接修改state导致的更新延迟。
- 初始化筛选值:将
categoryValue和sdlrValue初始化为"00",避免初始值为undefined导致的筛选逻辑异常。 - 修复选项异常:修正了分类选项重复的
value,去除了SDLR选项中的多余空格,避免匹配失败。 - 移除冗余useEffect:原来的两个useEffect被替换为useMemo,彻底解决了状态更新不及时和逻辑互相覆盖的问题。
内容的提问来源于stack exchange,提问作者thomasw_lrd
相关产品推荐
相关产品推荐

