Intro

This quick guide will cover the basics of using React-Redux. They’ll be using a regular create-react-app template, but I’ll be building using Vite.

npm create vite@latest # answer the questions
# OR
npm create vite@latest <name> -- --template react

Then install the packages for Redux:

npm install @reduxjs/toolkit react-redux

Creating the Store

We need to initialize the store and export it, while auto configuring the DevTools. // browser plugin

import { configureStore } from '@reduxjs/toolkit'
 
export default configureStore({
  reducer: {},
})

Providing the Store to React - Provider

The Provider component is a wrapper around our app similar to how Context is. Steps:

  1. Go to your App component
  2. Import store we created and import Provider from react-redux
  3. Wrap your App in Provider and pass store to it as a prop.

Alternatively, can wrap it where you use App component… Example:

<Provider store={store}>
	<App />
</Provider>

Creating a Redux State Slice

Create a folder in src for features, and then a folder for the corresponding feature and its slice, for example src/features/cooker/cookerSlice.js.

Slices require:

  • A string name
  • An initial state value
  • 1+ reducer functions to determine how state can be updated

Steps

  1. import createSlice
  2. Initialize the Slice and export it
  3. Configure the requirements mentioned above.
  4. Then export the generated Actions creators and Reducers functions.

“Redux requires that we write all state updates immutably, by making copies of data and updating the copies. However, Redux Toolkit’s createSlice and createReducer APIs use Immer inside to allow us to write “mutating” update logic that becomes correct immutable updates.”

Here’s their counter slice example:

import { createSlice } from '@reduxjs/toolkit'
 
export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0,
  },
  reducers: {
    increment: (state) => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. It
      // doesn't actually mutate the state because it uses the Immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes.
      // Also, no return statement is required from these functions.
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    },
  },
})
 
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
 
export default counterSlice.reducer

Add the Slice to the Store

Import the slice’s reducer and add it to the store. To do this, go back to store.js and define a reducer field, which is an object containing all the reducers. With the following the convention : reducerName: reducerFunction. Example:

import { configureStore } from '@reduxjs/toolkit'
import { cookerSlice } from '../features/cooker/cookerSlice'
 
export default configureStore({
  reducer: {
	  cooker: cookerSlice.reducer, 
  },
})

Using State and Actions in Components

Now that everything’s set up, we can read from the store and dispatch actions.

  • To read from the store use useSelector hook.
  • To dispatch action use the useDispatch hook. Create a React component and and use these hooks accordingly to read and update state. For example, here’s their counter.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'
import styles from './Counter.module.css'
 
export function Counter() {
  const count = useSelector((state) => state.counter.value)
  const dispatch = useDispatch()
 
  return (
    <div>
      <div>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(decrement())}
        >
          Decrement
        </button>
      </div>
    </div>
  )
}

Breakdown:

  1. All the imports, including the reducers from the Slice and Redux hooks.
  2. Assign the hooks to constants for easier use.
  3. Build the component that returns JSX
  4. When the button clicks, using the onClick event handler, we dispatch an action using dispacth(increment) or dispacth(increment).

The dispatched action is then reduced by the corresponding Reducer function in counterSlice.js, and the component will re-render with the new state.

Summary

  1. Create a Redux store with configureStore
    1. configureStore accepts a reducer function as a named argument
    2. configureStore automatically sets up the store with good default settings
  2. Provide the Redux store to the React application components
    1. Put a React Redux <Provider> component around your <App />
    2. Pass the Redux store as <Provider store={store}>
  3. Create a Redux “slice” reducer with createSlice
    1. Call createSlice with a string name, an initial state, and named reducer functions
    2. Reducer functions may “mutate” the state using Immer
    3. Export the generated slice reducer and action creators
  4. Use the React Redux useSelector/useDispatch hooks in React components
    1. Read data from the store with the useSelector hook
    2. Get the dispatch function with the useDispatch hook, and dispatch actions as needed