React Query:解决筛选条件变更时的UI闪烁问题
解决React Query分页筛选列表的UI闪烁问题
问题根源
你遇到的闪烁是因为筛选条件变化时,页码重置是异步的:用户切换筛选后,先触发了「新筛选+旧页码」的查询,随后useEffect才把页码改成1,此时UI会临时显示旧筛选条件下的第1页数据(因为placeholderData复用了旧缓存),直到新筛选的第1页数据加载完成,从而导致闪烁。
正确解决方案
1. 同步更新筛选条件与页码,移除异步副作用
把原来通过useEffect异步重置页码的逻辑,改成在用户触发筛选变化时同步更新,避免中间状态的出现:
function PatientList() { const [page, setPage] = useState(1); const [filters, setFilters] = useState<PatientFilters>(); const pageSize = 10; // 替换原来的useEffect,在设置筛选时直接重置页码 const updateFilters = (newFilters: PatientFilters) => { setFilters(newFilters); setPage(1); // 同步更新,确保新查询直接用第1页参数 }; const { data, isLoading, isFetching } = usePatients({ page, pageSize, filters }); return ( <div> {/* 筛选组件调用updateFilters,而不是直接setFilters */} <FilterComponent onChange={updateFilters} /> {/* 加载状态处理:避免显示旧数据 */} {(isLoading || isFetching) && <div>加载中...</div>} {data && !isLoading && ( // 渲染列表内容 <PatientTable data={data} /> )} </div> ); }
2. 优化查询Hook:简化参数与QueryKey
修改usePatients Hook,直接接收page和pageSize,而不是计算后的skip,让QueryKey更清晰,避免缓存混乱:
export function usePatients({ initialData, filters, page = 1, pageSize = 10, }: { initialData?: TFetchResponse<Patient>; page?: number; pageSize?: number; filters?: PatientFilters; }) { const skip = (page - 1) * pageSize; return useQuery({ // QueryKey直接包含page、pageSize、filters,确保不同组合对应不同缓存 queryKey: ['patients', page, pageSize, JSON.stringify(filters)], queryFn: async () => { const response = await patientFindManyAction({ skip, take: pageSize, filters, }); if (!response?.data) throw new Error('请求失败'); return response.data; }, // 仅首次加载用initialData,筛选变化时不复用旧缓存 placeholderData: initialData, }); }
3. 禁用旧数据复用(关键)
去掉原来placeholderData中复用previousData的逻辑,这样当筛选条件变化时,新查询会直接进入加载状态,不会显示旧筛选下的任何数据,彻底避免闪烁。
额外优化:稳定筛选条件引用
如果PatientFilters是复杂对象,每次更新时尽量保持引用稳定(比如用useMemo包裹),或者在QueryKey中用JSON.stringify(filters),确保React Query能正确识别筛选条件的变化,避免不必要的重复查询。
内容的提问来源于stack exchange,提问作者Ric
相关产品推荐
相关产品推荐

