Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react to multiple actions in redux toolkit's createSlice

I'm refactoring my reducers to use the redux-toolkit's createSlice. Now I have a very event-based reducer and sometimes a similar state update for different actions is required . With the original switch/case statement this was no issue:

        case ActionTypes.CREATION_CANCELLED:
        case ActionTypes.NEW_MARKER_INFOWINDOW_CLOSED:
        case ActionTypes.MARKER_SELECTED:
            return {...state, isCreating: false};

Is this kind of behaviour possible with a createSlice function?

like image 767
sjbuysse Avatar asked Sep 26 '20 19:09

sjbuysse


1 Answers

You can do this using the extraReducers "builder callback" notation. With this notation you create a reducer by adding cases to a builder object. The first argument of the builder.addMatcher function determines which action types match this case.

If your actions share a common string format, you can use string-based matchers like action.type.endsWith('/rejected') to handle all rejected actions the same way.

Otherwise you can define your own custom matcher function that takes an action and returns a boolean of whether or not it's a match.

Since we want to match multiple action types, we can create a helper for that.

// helper function to match any actions in a provided list
// actions can be `string` types or redux-toolkit action creators
const isAnyOf = (...matchers: Array<string | { type: string }>) => 
  ( action: AnyAction ) =>
    matchers.some((matcher) =>
      typeof matcher === "string"
        ? matcher === action.type
        : matcher.type === action.type
    );

We can use this with your existing string constants:

const slice = createSlice({
  name: "some name",
  initialState: {
    someProp: [],
    isCreating: false
  },
  reducers: {},
  extraReducers: (builder) => {
    return builder.addMatcher(
      isAnyOf(
        ActionTypes.CREATION_CANCELLED,
        ActionTypes.NEW_MARKER_INFOWINDOW_CLOSED,
        ActionTypes.MARKER_SELECTED
      ),
      (state, action) => {
        state.isCreating = false;
      }
    );
  }
});

Or with Redux Toolkit action creators:

const creationCancelled = createAction("CREATION_CANCELLED");
const newMarkerInfoWindowClosed = createAction("NEW_MARKER_INFOWINDOW_CLOSED");
const markerSelected = createAction("MARKER_SELECTED");

const slice = createSlice({
  name: "some name",
  initialState: {
    someProp: [],
    isCreating: false
  },
  reducers: {},
  extraReducers: (builder) => {
    return builder.addMatcher(
      isAnyOf(creationCancelled, newMarkerInfoWindowClosed, markerSelected),
      (state, action) => {
        state.isCreating = false;
      }
    );
  }
});
like image 135
Linda Paiste Avatar answered Oct 20 '22 06:10

Linda Paiste