如何在useRuns请求完成runs更新时同步更新currentRuns?
问题:当useRuns请求完成且runs更新时,如何同步更新currentRuns?
问题详情
- currentRuns初始被设置为useRuns中runs的初始空数组
- useRuns请求完成后,currentRuns未自动更新
- 因currentRuns为空,runTable无内容显示
- 期望useRuns的runs更新时,currentRuns的初始状态也随之同步更新
相关代码
Runs.ts
function useRuns(count: number) { const [runs, setRuns] = useState<IRun[]>([]) useEffect(() => { const getRuns = async () => { try { const runs = await request<IRun[]>(/* 指定count的请求参数 */) setRuns(runs) } catch (error) { /* 错误日志处理 */ } } getRuns() }, [count]) const runsSummary = useMemo(() => { /* 计算run汇总数据 */ }, [runs]) const runTuple: [IRun[], IRunsSummary] = [runs, runsSummary] return runTuple }
LogViewer.tsx
function LogViewer() { const [runs, runsSummary] = useRuns(runCount); const [currentRuns, setCurrentRuns] = useState<IRun[]>(runs) const addFilter = (columnName: keyof IRun) => (value: string | string[]) => { const filteredRuns = runs.filter(run => { /* 过滤逻辑 */ }) setCurrentRuns(filteredRuns) } return ( <div> {/* 使用addFilter的其他组件 */} <RunTable runs={currentRuns} /> {/* 其他内容 */} </div> ) }
解决方法
方法1:监听runs变化重置currentRuns
在LogViewer组件中添加useEffect,监听runs的更新,当runs从请求返回新数据时自动重置currentRuns:
function LogViewer() { const [runs, runsSummary] = useRuns(runCount); const [currentRuns, setCurrentRuns] = useState<IRun[]>(runs) // 新增:监听runs变化,同步更新currentRuns useEffect(() => { setCurrentRuns(runs); }, [runs]); const addFilter = (columnName: keyof IRun) => (value: string | string[]) => { const filteredRuns = runs.filter(run => { /* 过滤逻辑 */ }) setCurrentRuns(filteredRuns) } return ( <div> {/* 使用addFilter的其他组件 */} <RunTable runs={currentRuns} /> {/* 其他内容 */} </div> ) }
原理:useState的初始值仅在组件首次渲染时生效,后续runs更新不会自动同步到currentRuns。通过useEffect监听runs依赖,每次runs变化时调用setCurrentRuns,即可实现同步。
方法2:移除currentRuns状态,用useMemo计算过滤结果
如果过滤逻辑仅依赖runs和过滤条件,可以把过滤条件存为状态,用useMemo实时计算currentRuns,避免维护额外状态:
function LogViewer() { const [runs, runsSummary] = useRuns(runCount); // 存储过滤条件,示例:key为列名,value为过滤值 const [filters, setFilters] = useState<Record<keyof IRun, string | string[]>>({}); // 用useMemo计算过滤后的结果 const currentRuns = useMemo(() => { return runs.filter(run => { // 根据filters中的条件进行过滤 return Object.entries(filters).every(([key, value]) => { const runValue = run[key as keyof IRun]; if (Array.isArray(value)) { return value.includes(runValue as string); } return runValue === value; }); }); }, [runs, filters]); const addFilter = (columnName: keyof IRun) => (value: string | string[]) => { setFilters(prev => ({ ...prev, [columnName]: value })); } return ( <div> {/* 使用addFilter的其他组件 */} <RunTable runs={currentRuns} /> {/* 其他内容 */} </div> ) }
这种方式更符合React状态管理逻辑,避免了状态不同步问题,同时减少了手动调用setCurrentRuns的代码。
方法3:将过滤逻辑移至RunTable组件
如果过滤逻辑仅服务于RunTable,可以直接把runs和过滤条件传入RunTable,在组件内部完成过滤:
// 修改LogViewer.tsx function LogViewer() { const [runs, runsSummary] = useRuns(runCount); const [filters, setFilters] = useState<Record<keyof IRun, string | string[]>>({}); const addFilter = (columnName: keyof IRun) => (value: string | string[]) => { setFilters(prev => ({ ...prev, [columnName]: value })); } return ( <div> {/* 使用addFilter的其他组件 */} <RunTable runs={runs} filters={filters} /> {/* 其他内容 */} </div> ) } // RunTable.tsx function RunTable({ runs, filters }: { runs: IRun[], filters: Record<keyof IRun, string | string[]> }) { const filteredRuns = useMemo(() => { return runs.filter(run => { // 过滤逻辑 return Object.entries(filters).every(([key, value]) => { const runValue = run[key as keyof IRun]; if (Array.isArray(value)) { return value.includes(runValue as string); } return runValue === value; }); }); }, [runs, filters]); return ( {/* 渲染表格内容,使用filteredRuns */} ) }
这种方式让组件职责更清晰,LogViewer专注于数据获取和过滤条件管理,RunTable专注于表格渲染和数据过滤。
内容的提问来源于stack exchange,提问作者Harrison Waala
相关产品推荐
相关产品推荐

