Redux Toolkit

yarn add react-redux @reduxjs/toolkit

includes the Redux core, as well as other key packages like(Redux Thunk and Reselect, immer) and helper functions

Configure Store

  • A configureStore() function with simplified configuration options. It can automatically combine your slice reducers, adds whatever Redux middleware you supply, includes redux-thunk by default, and enables use of the Redux DevTools Extension.

const store = configureStore({
  reducer: counter
})

Create Slice

  • A createSlice() function that accepts a set of reducer functions, a slice name, and an initial state value, and automatically generates a slice reducer with corresponding action creators and action types. Default(unknown action type) returns current.

    • Mutations of state will auto create a new object on change as needed

import { createSlice } from "@reduxjs/toolkit";

const counterSlice = createSlice({
  name: "counter",
  initialState: 0,
  reducers: {
    increment: (state) => state + 1,
    decrement: (state) => state - 1,
    //called when dispatch type counter/addTodo
    addTodo(state, action) {
      const { id, text } = action.payload;
      state.push({ id, text, completed: false });
      //allowed to mutuate because function wrapped with produce from Immer library
    },
  },
});

export const { increment, decrement, addTodo } = todosSlice.actions;

export default todosSlice.reducer;

Old Helpers but unneeded b/c create Slice

  • A createReducer() utility that lets you supply a lookup table of action types to case reducer functions, rather than writing switch statements. In addition, it automatically uses the immer library to let you write simpler immutable updates with normal mutative code, like state.todos[3].completed = true.

//OLD
function counter(state = 0, action) {
  switch (action.type) {
    case increment.type:
      return state + 1
    case decrement.type:
      return state - 1
    default:
      return state
  }
}

//NEW
const increment = createAction('INCREMENT')
const decrement = createAction('DECREMENT')

const counter = createReducer(0, {
  [increment.type]: state => state + 1,
  [decrement.type]: state => state - 1
})
  • A createAction() utility that returns an action creator function for the given action type string. The function itself has toString() defined, so that it can be used in place of the type constant.

const increment = createAction('INCREMENT')
const decrement = createAction('DECREMENT')

//With payload generation helper, must return object with payload
export const addTodo = createAction('ADD_TODO', text => {
  return {
    payload: { id: nextTodoId++, text }
  }
})

const counter = createReducer(0, {
  [increment]: state => state + 1,
  [decrement]: state => state - 1
})

const store = configureStore({
  reducer: counter
})

document.getElementById('increment').addEventListener('click', () => {
  store.dispatch(increment())
})
  • However, we can take advantage of createAsyncThunk to clean up the above code, and to create those three actions automatically:

    const usersSlice = createSlice({
      name: 'users',
      initialState: { entities: [], loading: 'idle' },
      reducers: {
        // standard reducer logic, with auto-generated action types per reducer
      },
      extraReducers: {
        // Add reducers for additional action types here, and handle loading state as needed
        [fetchUserById.fulfilled]: (state, action) => {
        // Add user to the state array
          state.entities.push(action.payload)
      }
      }
    })
    
    const getUsers = createAsyncThunk("users/getUsers", () => {
      return fetch("/api/users/")
        .then(response => {
          if (!response.ok) throw Error(response.statusText);
          return response.json();
        })
        .then(json => json);
    });

    What createAsyncThunk does here, is to automatically create an action creator for each Promise state. For example if we name our async thunk "users/getUsers", it generates:

    • pending: "users/getUsers/pending"

    • rejected: "users/getUsers/rejected"

    • fulfilled: "users/getUsers/fulfilled"

Last updated