如何在 redux 中使用 thunk 解决异步链接

How to resolve async chaining using thunk in redux

我试图在 Redux 中使用 thunk 同时发送两个 post,但是当我尝试发送第二个 post 时,我丢失了前一个 [=18] 的用户 ID =].可能有一个关于如何异步链接这些请求的过程,但我还没有找到好的解决方案。 userSelector 似乎应该保持其状态。我也试过这个link:https://blog.jscrambler.com/async-dispatch-chaining-with-redux-thunkPromise.all 不推荐,但我也尝试过 dispatch(…).then(() => dipatch())。任何反馈都很棒!!

import { configureStore, createAsyncThunk, createSlice, unwrapResult } from '@reduxjs/toolkit';

export const postInfo = createAsyncThunk(
  'info/postInfo'
  async (infoObject: InfoRequest) => {
    const response = await axios.post(`${url}/info`, infoObject);
    return response.data;
  }
);

export const postSales = createAsyncThunk(
  'sales/postSales'
  async (salesObject: SalesRequest) => {
    const response = await axios.post(`${url}/sales`, salesObject);
    return response.data;
  }
);
...
const postInfoSlice = createSlice<PostState, SliceCaseReducers<PostState>, string>({
  name: 'postInfo',
  sales: 'postSales',
  initialState: {
    request: { status: 'idle', error: '' },
  },
  reducers: {},
  extraReducers: (builder) => {
      builder.addCase(postInfo.fulfilled, (state, action) => {
        state.request.status = 'succeeded';
        state.model = action.payload;
      }
      builder.addCase(postInfo.rejected, (state, action) => {
        state.request.status = 'failed';
        state.request.error = action.error.message as string;
      })
      builder.addCase(postSales.fulfilled, (state, action) => {
        state.request.status = 'succeeded';
        state.model = action.payload;
      }
      builder.addCase(postSales.rejected, (state, action) => {
        state.request.status = 'failed';
        state.request.error = action.error.message as string;
      })
   },
})

...

const store = configureStore({
    reducer:
        postInfoSlice.reducer
});
export type RootState = ReturnType<typeof store.getState>;
constant user = useSelector((state: RootState) => state.postState.user);

const sendInfoRequest = async () => {
  try {
    const infoObjRequest: InfoRequest = {
      firstName: 'John',
      lastName: 'Smith'
    };
    await dispatch(postInfo(infoObjRequest)).unwrap();
  } catch (err) {    
       console.log('rejected for post /info', err);
  }
};

const sendSalesRequest = async () => {
  try {
    const salesObjRequest: SalesRequest = {
      firstName: 'John',
      lastName: 'Smith',
      userId: user?.id
    };
    await dispatch(postSales(salesObjRequest)).unwrap();
  } catch (err) {    
       console.log('rejected for post /sales', err);
  }
};

// Here is where I am dispatching both post where sendSalesRequest can't get the user id from previous postInfo. 

sendInfoRequest();
sendSalesRequest();

为什么不在请求中将 ID 作为变量传递?

//is user null or not initialized?
sendInfoRequest(user?.id);
sendSalesRequest(user?.id);

显式发送内容没有错——尤其是在异步上下文中(也更容易测试)。

我认为更大的问题是你的状态可能不是你所期望的(比如 null)。根据我的经验,让事情尽可能简单明确是最有意义的。

回复评论 我在理解你的问题时遇到一些困难,但如果我理解你基本上想要做的逻辑:

 -> Send request -> get ID -> use ID

从根本上说,如果不知道 ID,就无法预先完成。您可能想要的是:

-> Send request (wait)
  -> with data do {
        action1, action2, etc...
  }

没有足够的代码可以为您提供除此之外的任何真实信息。如果用户 ID 在您所在的州不存在,您需要请求并使用它。在 redux 中通常看起来像

//And please forgive me, there are A LOT of different ways to write this
...
const doAfterUserIdExists = (userId) => {
  dispatch(a)
  dispatch(b)
  ...
  dispatch(x)
}
dispatch( initialAction(doAfterUserIdExists) )

//--in the backend
export const initialAction = (callback) => {
   return dispatch => {
       //do some business logic
       ...
       const user = new User()//ID is created
       if(callback) {
         callback(user)
       }

       dispatch({
         type: CASCADE_USER_FUNCTION,
         user: user,
       })
   }
}

这与您正在做的并没有什么不同,只是它具有线性流程。 Promise.all() 也不可行,因为它会同时 运行 您的所有事件(毫无意义,您首先需要一个 ID)。

这不是一个完美的解决方案,但它可以让您了解如何控制数据流。您还可以研究 Sagas 或其他模式以使“thunks”起作用。或者,您可以翻转它,以便发布信息和销售请求等“子逻辑”在 back-end 中发生(如果它们是交易的一部分)。

这并不神奇,您需要找到适合您的解决方案。我倾向于依靠回调,因为它们是线性流程,但有许多不同的模式。我发现这个最容易阅读。