useList钩子本地构建正常,生产环境数据被覆盖问题排查
结合你描述的现象(本地开发正常、生产环境下useList第二次运行清空数据),以下是常见原因及解决思路:
1. 未缓存过滤后的数组导致重复更新
如果你的过滤逻辑没有用useMemo缓存结果,每次组件渲染都会生成新的数组引用。React-Admin的useList会监听传入的data变化,频繁的新引用会触发多次状态更新——开发环境下React的严格模式或调试延迟会掩盖这个问题,但生产环境下代码优化后,第二次更新可能刚好赶上Context数据未稳定的时机,导致空数据覆盖。
修复方式:用useMemo缓存过滤结果,只有当源数据变化时才重新计算:
import { useMemo } from 'react'; // ... const filteredData = useMemo(() => { return contextData?.filter(item => /* 你的过滤条件 */) || []; }, [contextData]);
2. Context数据的异步加载时序问题
本地开发时,要么用的是同步mock数据,要么API请求延迟极低,Context数据能在组件首次渲染时就绪。但生产环境下,API请求可能有延迟,导致组件先以空/初始Context数据渲染(第一次输出符合预期可能是你看到的是加载后的状态),之后Context状态更新时,过滤逻辑因某种原因返回空数组,进而触发useList的第二次更新。
排查方向:
- 检查Context的状态更新逻辑,确认生产环境下API返回的数据格式、内容和本地一致;
- 添加错误处理,避免API请求失败时Context数据被意外清空;
- 在组件中判断Context数据是否就绪,再执行过滤和
useList调用:if (!contextData) return <Loading />; const filteredData = useMemo(() => contextData.filter(...), [contextData]); const listContext = useList({ data: filteredData, total: filteredData.length });
3. useList内部状态与手动传入数据的冲突
useList本身会管理分页、排序、过滤等状态,如果你同时启用了它的默认数据获取逻辑(比如通过dataProvider)又手动传入data,生产环境下的代码压缩会让内部状态更新的优先级变高,导致自动请求的空数据覆盖了你手动传入的过滤结果。
修复方式:禁用useList的自动数据请求,完全使用手动传入的数据:
const listContext = useList({ data: filteredData, total: filteredData.length, // 禁用自动鉴权和请求 disableAuthentication: true, queryOptions: { enabled: false }, });
4. Vite生产打包优化的影响
Vite在生产模式下会做Tree Shaking、变量提升、代码压缩等优化,可能改变组件或Context的执行顺序。比如你的过滤逻辑被提前执行,此时Context数据还未初始化,返回空数组;后续Context数据加载完成后,useList的状态已经被空数据覆盖。
排查方向:
- 用条件编译添加生产环境日志,打印
contextData、filteredData的变化及useList的执行时机:if (import.meta.env.PROD) { console.log('Context data:', contextData); console.log('Filtered data:', filteredData); } - 检查Vite配置,暂时关闭部分优化(比如
treeShaking: false),验证是否是优化导致的问题。
内容的提问来源于stack exchange,提问作者Heikkisorsa

