如何在同一个Slice中从Reducer方法调用AsyncThunk方法

Han*_*ank 5 typescript reactjs redux react-redux redux-toolkit

我有一个使用 reduxjs/toolkit 的切片,状态包含一个 ServiceRequest 对象和一个 ServiceRequest 数组。

我想要实现的是;在加载组件时,我想分派对减速器的调用,该减速器通过 id 检查 ServiceRequest 是否已存在于数组中,如果存在,则使用找到的对象填充 ServiceRequest,如果不存在,则调用同一中的 AsyncThunk 方法切片以从 WebAPI 检索它。

我无法弄清楚是从reducer 或reducer 方法内部调用AsyncThunk 方法。也许不应该这样做,但这似乎是一个将所有东西放在一起的好地方。

我怎样才能实现这个目标?

这就是我到目前为止所拥有的:(您会看到我认为应该调用 AsyncThunk 方法的地方已被注释掉)

import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
import { ServiceRequest } from "./models/ServiceRequest.interface";
import csmRequestDataService from "./services/csmRequestDataService";

interface AsyncState {
  isLoading: boolean;
  isSuccess: boolean;
  isError: boolean;
}

interface CSMState extends AsyncState {
  serviceRequest: ServiceRequest | null;
  serviceRequests: ServiceRequest[];
}

const initialState: CSMState = {
  isLoading: false,
  isSuccess: false,
  isError: false,
  serviceRequest: null,
  serviceRequests: [],
}

export const getServiceRequest = createAsyncThunk(
  'csm/getServiceRequest',
  async (serviceRequestId: number) => {
    try {
      console.log('getServiceRequest');
      return await csmRequestDataService.getServiceRequest(serviceRequestId);
    } catch (error) {
      console.log('Error: ', error);
    }
  });

const getOpenedServiceRequests = (
  serviceRequests: ServiceRequest[],
  serviceRequestId: number
) => {
  const serviceRequest = serviceRequests.find(
    (tsr) => tsr.ServiceRequestId === serviceRequestId
  ) || null;
  /*
  if (serviceRequest == null) {
    console.log('GET REQUEST FROM API');
    getServiceRequest(serviceRequestId);
  } else {
    console.log('GOT REQUEST FROM STORE')
  }
  */
  return serviceRequest;
};

export const csmRequestDataSlice = createSlice({
  name: ' csmRequestData',
  initialState,
  reducers: {
    retrieveServiceRequest: (state, action: PayloadAction<number>) => {
      const serviceRequest = getOpenedServiceRequests(
        state.serviceRequests,
        action.payload
      );
      state.serviceRequest = serviceRequest;
      /*
      if (serviceRequest == null) {
        console.log('GET REQUEST FROM API');
        getServiceRequest(action.payload);
      } else {
        console.log('GOT REQUEST FROM STORE')
      }
      */
    }
  },
  extraReducers(builder) {
    builder
      .addCase(getServiceRequest.pending, (state) => {
        state.isLoading = true;
      })
      .addCase(getServiceRequest.fulfilled, (state, action) => {
        if (action.payload && action.payload.serviceRequest !== null) {
          state.serviceRequests.push({ ...action.payload.serviceRequest });
          state.serviceRequest = action.payload.serviceRequest;
        }

        state.isLoading = false;
        state.isSuccess = true;
        console.log('got request data');
      })
      .addCase(getServiceRequest.rejected, (state) => {

        state.isLoading = false;
        state.isError = true;
      })
  },
});

export const { retrieveServiceRequest } = csmRequestDataSlice.actions;
export default csmRequestDataSlice.reducer;
Run Code Online (Sandbox Code Playgroud)

当取消注释在任一位置对 getServiceRequest 的调用时,它似乎没有执行任何操作,我在想也许像从组件中那样进行调度可能会起作用,但不确定如何实现它。

更新:

以下是从切片调用的服务:

const getServiceRequest = async (serviceRequestId: number) => {

  const response = await axiosConfig.get(
    'api/csm/getServiceRequest/' + serviceRequestId
  );

  if (response.data) {
    console.log('got service request data in service');
    return { serviceRequest: response.data };
  }

  return { serviceRequest: null };
}

const csmRequestDataService = {
  getServiceRequest,
}

export default csmRequestDataService;
Run Code Online (Sandbox Code Playgroud)

更新2:

我改变了 getOpenedServiceRequests:

export const getOpenedServiceRequests = createAsyncThunk(
  'csm/getOpenedServiceRequests',
  async (
    serviceRequestId: number,
    { dispatch, getState }
  ) => {

    const state: any = getState();

    const { serviceRequests } = state.csmRequestDataReducer;

    let serviceRequest = serviceRequests.find(
      (tsr: ServiceRequest) => tsr.ServiceRequestId === serviceRequestId
    ) || null;

    if (!serviceRequest) {
      const payloadAction: any =  await dispatch(getServiceRequest(serviceRequestId));
      serviceRequest = payloadAction.payload.serviceRequest
      console.log('***********Retieved SeriveRequest from API');
    } else {
      
      console.log('***********Retieved SeriveRequest from array');
    }

    return { serviceRequest: serviceRequest };
  }
);
Run Code Online (Sandbox Code Playgroud)

如您所见,我添加了 async/await 并解压了 getServiceRequest 结果。

PayloadAction结果如下:

在此输入图像描述

这工作得很好,除了解包时感觉有点混乱之外,我还需要在额外的减速器中添加一个标志来说明服务请求是否添加到 ServiceRequests 数组中。

我的想法是,不调用dispatch(getServiceRequest(serviceRequestId))而是直接调用服务,那么我不需要解压/重新打包结果,我还可以在每个结果中设置一个标志,说明是否应将服务请求添加到数组中

Dre*_*ese 4

减速器函数应被视为纯粹的同步函数。它们是前一个状态和动作的同步函数,并返回下一个状态。您正在寻找或要求的是另一个异步操作,它执行检查并有条件地分派另一个异步操作。

转换getOpenedServiceRequests为 Thunk 并访问有效负载创建函数的第二thunkAPI参数。用于getState获取完整的状态对象,并dispatch调度进一步的操作,例如getServiceRequest.

import {
  createAsyncThunk,
  createSlice,
  PayloadAction
} from "@reduxjs/toolkit";
import { ServiceRequest } from "./models/ServiceRequest.interface";
import csmRequestDataService from "./services/csmRequestDataService";
Run Code Online (Sandbox Code Playgroud)

...

export const getServiceRequest = createAsyncThunk(
  "csmRequestData/getServiceRequest",
  async (serviceRequestId: number, { rejectWithValue }) => {
    try {
      return await csmRequestDataService.getServiceRequest(serviceRequestId);
    } catch (error) {
      console.warn('Error: ', error);
      rejectWithValue(error);
    }
  },
);

export const getOpenedServiceRequests = createAsyncThunk(
  "csmRequestData/getOpenedServiceRequests",
  (serviceRequestId: number, { dispatch, getState }) => {
    // get the current complete state
    const state = getState();

    // access into state to get the serviceRequests array
    const { serviceRequests } = state.csmRequestData; // <-- * NOTE

    const serviceRequest = serviceRequests.find(
      (tsr) => tsr.ServiceRequestId === serviceRequestId
    );
    
    if (!serviceRequest) {
      // No service request, dispatch action to get it
      const { payload } = await dispatch(getServiceRequest(serviceRequestId));
      return { serviceRequest: payload.serviceRequest };
    }
    
    // Fulfill with found service request
    return { serviceRequest };
  },
);
Run Code Online (Sandbox Code Playgroud)
export const csmRequestDataSlice = createSlice({
  name: 'csmRequestData',
  initialState,
  extraReducers(builder) {
    builder
      .addCase(getOpenedServiceRequests.pending, (state) => {
        state.isLoading = true;
      })
      .addCase(getOpenedServiceRequests.fulfilled, (state, action) => {
        const { serviceRequest } = action.payload;
        if (serviceRequest) {
          state.serviceRequests.push({ ...serviceRequest });
          state.serviceRequest = serviceRequest;
        }

        state.isLoading = false;
        state.isSuccess = true;
      })
      .addCase(getOpenedServiceRequests.rejected, (state) => {
        state.isLoading = false;
        state.isError = true;
      });
  },
});

export default csmRequestDataSlice.reducer;
Run Code Online (Sandbox Code Playgroud)

*注意:在这里,您需要访问全局状态对象,遵循您组合减速器并形成状态树所创建的路径。