我正在开发一个 React 组件(FSRenderer),它获取和转换数据,然后渲染网格和标题。我的问题与 Redux 管理的加载状态以及渲染行为有关。
事情是这样的:
当获取和转换数据时,我使用dispatch(setLoading(true))将Redux加载状态设置为true。
转换完成后,我设置转换后的数据(transformedData)、转换后的配置(transformedConfig),最后使用dispatch(setLoading(false))将Redux加载状态更新为false。
但是,我注意到在transformedData和transformedConfig更新之前加载状态就变成了false。
因此,尽管转换随后立即完成,但网格暂时不显示任何数据时会发生额外的渲染。
const [transformedData, setTransformedData] = useState<any[]>([]);
const [transformedConfig, setTransformedConfig] = useState<Object>({});
const gridRef = useRef();
useEffect(() => {
(async () => {
try {
dispatch(setLoading(true));
const data = await dmServices.getGridData();
setTransformedData(data);
setTransformedConfig(data);
dispatch(setLoading(false)); // This seems to execute before `setTransformedData` and `setTransformedConfig`
} catch (err) {
console.error('Error while fetching and transforming data:', err);
dispatch(setLoading(false));
}
})();
}, [module?.id, filters]);
return (
<div>
{transformedData.length > 0 && <Grid config={transformedConfig} data={transformedData} autosizeAllColumns={true} id='Grid' ref={gridRef} />}
{transformedData.length == 0 && !gridLoading && <NoDataAvailable />}
</div>
);
有什么办法可以解决这个问题吗?我可以保留一个本地状态变量来跟踪加载,这会很好地工作。但我不想有 2 种加载状态 - 一种在减速器中,一种在本地状态
基本要点是排队的 React 状态更新不会立即处理,而分派到 Redux 存储的操作会立即处理。它们都会触发组件重新渲染,但因为 Redux 更新首先被处理,所以它们的更新会在 React 状态更新被处理并触发其重新渲染之前触发重新渲染。
在
await dmServices.getGridData()
之后,回调函数作用域中没有其他内容是异步的,因此函数体的其余部分将同步处理。
以下是操作和状态更新的一些输出日志记录示例:
// initial state {gridLoading: false, transformedData: Array(0)} // effect runs dispatch loading true // <-- update Redux {gridLoading: true, transformedData: Array(0)} // <-- Redux store updated // Got data enqueue React state update // <-- update local State dispatch loading false // <-- update Redux {gridLoading: false, transformedData: Array(0)} // <-- Redux store updated {gridLoading: false, transformedData: Array(1)} // <-- Local state updated
作为一点“黑客”,你可以插入一些额外的异步逻辑,以允许 React 处理排队状态更新然后将操作分派到存储。
示例:
useEffect(() => {
(async () => {
try {
dispatch(setLoading(true));
const data = await dmServices.getGridData();
setTransformedData(data);
setTransformedConfig(data);
// "Sleep" for the minimal amount of time and place the rest of
// this function to the back of the event queue, allows above
// React state updates to be processed first.
await new Promise(resolve => {
setTimeout(resolve, 0);
});
} catch (err) {
console.error('Error while fetching and transforming data:', err);
} finally {
dispatch(setLoading(false));
}
})();
}, [module?.id, filters]);
以下是操作和状态更新的类似输出日志记录,其中 Redux 更新被推送到事件队列的后面:
// initial state {gridLoading: false, transformedData: Array(0)} // effect runs dispatch loading true // <-- update Redux {gridLoading: true, transformedData: Array(0)} // <-- Redux store updated // Got data enqueue React state update // <-- update local State {gridLoading: true, transformedData: Array(1)} // <-- Local state updated dispatch loading false // <-- update Redux {gridLoading: false, transformedData: Array(1)} // <-- Redux store updated