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

ThreadPoolExecutor 挂起

vmware虚拟机怎么开机黑屏,无法关机挂起

redux 工具包查询和创建异步 thunk 有啥区别?

无法从连接的 React 组件触发 createAsyncThunk

使用axios时如何在createAsyncThunk中创建payload