【问题标题】:createAsyncThunk - changing the state on pending doesn't cause immediate rerenderingcreateAsyncThunk - 更改挂起状态不会导致立即重新渲染
【发布时间】: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;

【问题讨论】:

    标签: reactjs react-redux react-hooks redux-thunk redux-toolkit


    【解决方案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 位了)。再次感谢。
    猜你喜欢
    相关资源
    最近更新 更多
    热门标签