Redux in React.js — Intro

Justin Langlinais
4 min readOct 19, 2020

--

Photo by Florencia Viadana on Unsplash

Welcome to my first in a series of posts I’ll be making on learning Redux.

To start, what is Redux?

Redux is a state management tool that allows us to access and modify state across our app without having to pass it along to components as props. As our apps grow, our component trees can become very deep, and passing a piece of state down as props through several components can become very hard to maintain and debug. Redux creates a global state (called the ‘store’) that can easily be accessed throughout the project. This grants us the ability to use a single source of truth. Additionally, we can update data in the store using dispatch actions.

How do we access state?

We will wrap our project with react-redux’s Provider, which makes our store available throughout the app. Then, we will be able to use Redux functions to map state as props in components, which can be read as though they’re receiving them from a parent component.

How will we update state?

To update our state, or any piece of it, we will send an action, which will be defined by us. An action is a plain JavaScript object that has a .type attribute. The type should be a descriptive string, like “count/incrementBy1,” “ingredients/boilPotato,” or “users/addNewUser.” The first part is the feature or category the action belongs to, and the second part is what we want to happen.

const incrementBy1Action = {
type: 'count/incrementBy1'
}

Where are we sending our action to update state?

We send our actions to a reducer. This is a function that receives two arguments: our current state, and an action object. It will use a switch case, if else, or loops to update the state the way our action wants it to, and returns the new state:

counterReducer(state, action) => newState 

Reducers must follow some rues:

  • They should only calculate the new state value based on the state and action arguments
  • They are not allowed to modify the existing state. We will typically be using the spread operator (…state) to copy the existing state and then make the changes necessary, returning the new state
  • They must not do any asynchronous logic, calculate random values, or cause other “side effects” (Reducers are pure functions)

Here’s an example of a simple reducer, which is able to increment our count by 1:

function counterReducer( state = {count: 0}, action ) {
if (action.type === 'count/incrementBy1') {
return { ...state, count: state.count + 1 }
}
return state
}

So, we pass state and an action into our reducer (note that we’ve set a default/initial state count value of 0). The reducer uses an if statement to see if our action is asking it to increment by 1. If so, it returns the existing state with state.count incremented by 1. If the action we passed it wasn’t to increment by 1, it just returns state as-is. We can add more possible actions here, and could use a switch case instead of a series of if statements.

Now that we have an understanding of how a reducer works, let’s re-visit our store. Remember, the store is the object where our application’s state lives. With our App wrapped in <Provider />, it’s accessible throughout the app. But how is it created? The store is created by passing in a reducer. Let’s use the one we just built as an example:

import { configureStore } from '@reduxjs/toolkit'const store = configureStore({ reducer: counterReducer })

Now, store has a method called getState that returns the current state value.

The store also has a method called dispatch. To update state and persist the changes, we will call store.dispatch() and pass in an action object. The store runs its reducer function, updating the new value, and then we can call store.getState() to get the updated value:

console.log(store.getState())
// { count : 0 }
store.dispatch({ type: 'count/incrementBy1' })console.log(store.getState())
// { count : 1 }

To summarize:

  • We create the store using a reducer function. Upon creation, the store calls the reducer and saves the return value as its initial state.
  • When something happens in the app, like the user clicking in increment button, our code dispatches an action to the store, like store.dispatch({ type: 'count/incrementBy1' })
  • The store runs the reducer again, using the previous state and the current action as arguments, then assigns the return value as the new state.
  • The store then notifies all parts of the UI that are connected to it that state has been updated, triggering each of those components to re-render using the new state data.

Hopefully, this gives you a basic understanding of the information flow in Redux. Stay tuned for more exploration into Redux and its uses in our projects, and please reach out if you have any comments, tips, or questions!

--

--

Justin Langlinais
Justin Langlinais

Written by Justin Langlinais

Software Engineer and Brazilian Jiu-Jitsu brown belt

No responses yet