Search code examples
reactjsredux-toolkit

Redux Toolkit matching utilities (isAllOf) not capturing the state change


I'm following the example in the official doc Matching Utilities, and trying to use isAllOf to update state when all the API calls have been successfully made. All the parameters will be created by createAsyncThunk, i.e.:

export const fetchCart = createAsyncThunk(
  "api/fetchCart",
  async (payload, { rejectWithValue }) => {
    try {
      // Replace with your actual API call logic
      const response = await fetch("https://dummyjson.com/cart");
      const data = await response.json();
      return data;
    } catch (error) {
      return rejectWithValue(error.response.data);
    }
  },
);

and the way I'm calling it is similar to:

// src/store/pageStatusSlice.js
import { createSlice, isAllOf, isAnyOf, isRejected } from "@reduxjs/toolkit";
import { fetchCart } from "./cartSlice";
import { fetchProduct } from "./productSlice"; // Import your thunks

const pageStatusSlice = createSlice({
  name: "pageStatus",
  initialState: {
    isLoading: false,
    success: false,
    error: false,
  },
  extraReducers: (builder) => {
    builder
      .addMatcher(isAllOf(fetchProduct.pending, fetchCart.pending), (state) => {
        state.isLoading = true;
      })
      .addMatcher(
        isAllOf(fetchCart.fulfilled, fetchProduct.fulfilled),
        (state, action) => {
          state.isLoading = false;
          state.success = true;
        },
      )
      .addMatcher(
        isAnyOf(fetchCart.rejected, fetchProduct.rejected),
        (state, action) => {
          console.log(action);
          state.error = true;
        },
      );
  },
});

export default pageStatusSlice.reducer;

The rejection case is working, but not with the pending and fulfilled case, seems like both pending and fulfilled cases are not triggered.

When there is only 1 parameter passed in the isAllOf, it works fine.

I checked Error when using Redux toolkit's isAnyOf - TypeError: matcher is not a function, in the code snippet provided in this question, it seems like it's using the matching utilities in the same way:

builder
      .addMatcher(
        isAnyOf(getAllData.fulfilled, addQuestion.fulfilled, getQuestion.fullfilled, updateQuestion.fulfilled),
        (state) => {
          state.connectionError = null
        }
      )

The answer from the question above mentioned he accidently put .fulfilled at the end of the action and it was causing error, but in the official doc seems like using .fulfilled is allowed:

import { createReducer, isAllOf } from '@reduxjs/toolkit'
import {
  isSpecialAndInterestingThunk,
  initialState,
  isSpecial,
  isInteresting,
} from '@virtual/matchers' // This is a fake pkg that provides the types shown above

const loadingReducer = createReducer(initialState, (builder) => {
  builder
    .addMatcher(
      isAllOf(isSpecialAndInterestingThunk.fulfilled, isSpecial),
      (state, action) => {
        state.isSpecial = true
      }
    )
    .addMatcher(
      isAllOf(isSpecialAndInterestingThunk.fulfilled, isInteresting),
      (state, action) => {
        state.isInteresting = true
      }
    )
})

I have also created a code sandbox with a simple example: https://codesandbox.io/p/sandbox/admiring-dewdney-crdqdz?file=%2Fsrc%2Findex.js


Solution

  • isAllOf means "is all of these at the same time".

    So isAllOf(fetchProduct.pending, fetchCart.pending) means "it's an action for a pending fetchProduct thunk and a pending fetchCart thunk at the same time". Which is literally impossible.

    You probably want to use isAnyOf which means "is one of these things" instead.