简体   繁体   中英

Common loading state reducer with Redux toolkit

I'm working on an app where I have multiple slices. I'm using createAsyncThunk for API calls and I like it cause it provides action creators for different state of API request, so that I can track loading state and errors within the reducer. But my question is, what if I want to have a separate reducer to track loading , error and success of my API calls how do I accomplish that with redux-toolkit

I know I can dispatch an action from within my createAsyncThunk function but it doesn't feel right and kinda defeats the purpose of the function itself. Also, side effects inside the reducer are considered to be a bad practice. So, I'm kinda confused at this point, I want to have just one Loader component in the root of the app that gets triggered when the loading state is true and it doesn't matter what exactly is loading

Here is an example of my current code:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import { AxiosError } from 'axios'
import { masterInstance } from 'api'
import { GetAccessCodeParams, RegistrationStateType } from 'store/slices/registration/types'

export const getAccessCodeRequest = createAsyncThunk<void, GetAccessCodeParams, { rejectValue: { message: string } }>(
  'registration/getAccessCodeRequest',
  async ({ email }, { rejectWithValue }) => {
    try {
      await masterInstance.post(`/authorization/getAccessCodeWc`, { email })
    } catch (err) {
      let error: AxiosError = err
      if (error) {
        return rejectWithValue({
          message: `Error. Error code ${error.response?.status}`,
        })
      }
      throw err
    }
  }
)

const initialState: RegistrationStateType = {
  isLoading: false,
  error: null,
}

const registrationSlice = createSlice({
  name: 'registration',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(getAccessCodeRequest.fulfilled, (state) => {
      state.isLoading = false
      state.error = null
    })
    builder.addCase(getAccessCodeRequest.pending, (state) => {
      state.isLoading = true
      state.error = null
    })
    builder.addCase(getAccessCodeRequest.rejected, (state, action) => {
      if (action.payload) {
        state.error = {
          message: action.payload.message,
        }
      } else {
        state.error = action.error
      }
      state.isLoading = false
    })
  },
})

export const registrationReducer = registrationSlice.reducer

I want isLoading and error to be in a separate reducer

You could have a shared reducer matcher function.

// mySharedStuff.js

export const handleLoading = (action, (state) => {
  state.loading = action.type.endsWith('/pending'); // or smth similar
});

export const handleError = (action, (state) => {
  state.error = action.type.endsWith('/rejected'); // or smth similar
});

// mySlice.js

const mySlice = createSlice({
  name: 'FOO',
  initialState: {},
  reducers: {},
  extraReducers: builder => {
    builder.addMatcher(handleLoading),
    builder.addMatcher(handleError),
    ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM