Search code examples
reactjstypescriptreduxreact-reduxredux-toolkit

RTK Toolkit how to use builder syntax with action creators with createSlice?


I'm experienced in Redux but I'm totally new to Redux Toolkit (RTK) and createSlice. I'm trying to follow tutorials on official RTK Query docs but I'm getting lost.

I've created my actions: (assume there is a type called 'Model')

const setModel = createAction<Model>('setModel');

Then I've defined my state:

type MyState = {
  model: Model;
}

const initialState: MyState = {
  model: models[0], // points to a valid object of type Model
};

Then I try to create my slice:

export const mySlice = createSlice({
  name: 'x',
  initialState,
  reducers: {
        
  },
  extraReducers: builder => {
    builder.addCase(setModel, (state, action) => {
      state.model = action.payload;
      return state;
    });
  },
});

The docs seem to recommend the extraReducers approach to use with action objects and builder directly, which also seems to implicitly support Immer. So I wrote my simple case for setModel. However, when I try to use it in a component:

const dispatch = useDispatch();
dispatch(mySlice.actions.setModel(pickedItem));

mySlice.actions is empty and doesn't include an entry named setModel. It's not a TypeScript issue, putting a breakpoint when running also reveals that it's indeed empty:

enter image description here

Then I've read more and heard that extraReducers doesn't create action creators, whereas reducers does (which probably explains why my actions are empty). However, then I can't use the builder syntax which RTK Toolkit recommends in the first place.

At this point I'm totally lost.

How can I use createSlice with strongly-typed and autogenerated action creators, or if that's not possible, what's the recommended pattern for createSlice?


Solution

  • How can I use createSlice with strongly-typed and autogenerated action creators?

    You can type the payloads of the reducers in the slice, the actions will be generated for you, and be correctly typed.

    Example:

    import { createSlice } from '@reduxjs/toolkit';
    import type { PayloadAction } from '@reduxjs/toolkit';
    
    type MyState = {
      model: Model;
    }
    
    const initialState: MyState = {
      model: models[0],
    };
    
    const mySlice = createSlice({
      name: 'x',
      initialState,
      reducers: {
        setModel: (state, PayloadAction<Model>) => {
          state.model = action.payload;
        },
      },
    });
    
    export const {
      setModel
    } = mySlice.actions;
    
    export default mySlice.reducer;
    

    The extraReducers are primarily used to handle external actions, e.g. asynchronous actions and actions generated by other slices.