createAsyncThunk - 更改挂起状态不会导致立即重新渲染
Posted
技术标签:
【中文标题】createAsyncThunk - 更改挂起状态不会导致立即重新渲染【英文标题】:createAsyncThunk - changing the state on pending doesn't cause immediate rerendering 【发布时间】:2021-12-03 04:21:32 【问题描述】:我正在使用 typescript、useSelector 和 @reduxjs/toolkit...
...当我调度 computeConfidenceIntervalsAsync 操作(参见下面的代码)时,我可以看到代码立即运行以下行:
state.status = 'loading'
但是ViewComponent只有在payloadCreator运行完doSomeHeavyComputation函数后才会重新渲染。
用另一种方式解释它:在 ViewComponent 中,我希望在繁重的计算之前呈现“加载”状态,但由于某种原因,首先运行繁重的计算,然后我连续收到“加载”和“空闲” .
对这个有帮助吗?
减速机:
//...
export const computeConfidenceIntervalsAsync = createAsyncThunk(
'forecast/getConfidenceIntervals',
async (data: ConfidenceIntervalsParams) =>
const response = await getConfidenceIntervals(data.totalRuns, data.itemsTarget, data.throughputs, new Date(data.startingDate));
return [...response.entries()];
);
export const forecastSlice = createSlice(
name: 'forecast',
initialState,
reducers: ,
extraReducers: (builder) =>
builder
.addCase(computeConfidenceIntervalsAsync.pending, (state) =>
state.status = 'loading';
)
.addCase(computeConfidenceIntervalsAsync.fulfilled, (state, action) =>
state.status = 'idle';
state.confidenceIntervals = action.payload;
);
);
export const selectForecast = (state: RootState) => state.forecast;
//...
服务:
//...
export function getConfidenceIntervals(totalRuns: number, itemsTarget: number, throughputs: number[], startingDate: Date)
return new Promise<Map<string, number>>((resolve) =>
console.log('beginning');
const outcomes = doSomeHeavyComputation(totalRuns, itemsTarget, throughputs, startingDate);
console.log('ending');
resolve(outcomes);
);
//...
组件:
export function ViewComponent()
const forecast = useAppSelector(selectForecast);
console.log(forecast)
if (forecast.status === 'loading')
return (
<div>Loading...</div>
);
return (<div>...</div>);
使用选择器挂钩
import TypedUseSelectorHook, useDispatch, useSelector from 'react-redux';
import type RootState, AppDispatch from '../redux';
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
【问题讨论】:
【参考方案1】:同步工作是阻塞的 - 而不是 cAT
的设计目的。
您可以通过执行类似的操作来完成这项工作
export async function getConfidenceIntervals(totalRuns: number, itemsTarget: number, throughputs: number[], startingDate: Date)
await Promise.resolve() // this will defer this to the next tick, allowing React to render in-between
console.log('beginning');
const outcomes = doSomeHeavyComputation(totalRuns, itemsTarget, throughputs, startingDate);
console.log('ending');
return outcomes;
(我冒昧地将其重写为 async/await)
您的承诺会立即开始,之前没有暂停,所以我添加了一个 await Promise.resolve()
,它会延迟执行。
【讨论】:
谢谢!我认为您的解决方案已经解决了部分问题:现在我可以在繁重的同步工作开始之前在 ViewComponent 中运行此代码:if (forecast.status === 'loading') ...
但是由于某种原因,加载元素(Loading...) 尚未在浏览器中呈现。
React 本身可能会使用时间切片,将其工作分成多个部分,在它们之间运行您的代码。在开始工作之前,您可能需要设置一些超时。通常,尽管您应该避免在主线程上运行昂贵的工作或添加大量 await Promise.resolve()
以允许其他代码在其间运行。否则,您的浏览器会显示为死机。
setTimeout 工作得很好,但是,正如你所建议的,我最终使用网络工作者来完成这项昂贵的工作(不再需要 await Promise
位了)。再次感谢。以上是关于createAsyncThunk - 更改挂起状态不会导致立即重新渲染的主要内容,如果未能解决你的问题,请参考以下文章
Redux-Toolkit createAsyncThunk with Typescript