Basics of Redux

Abel Gechure
7 min readAug 16, 2021

For my final project at flatiron I used redux state management library to manage the state data in my project.

What is Redux ?

From my own understanding, redux is a state management library that we can use in react to help us manage and update state data using events called actions. Basically a container that holds the global state of the application making it easier for any component to access state data and hence avoiding prop drilling down the component tree usually seen in a react application.

Redux is more useful when you have

  1. Large amounts of application state that are needed in many places in the app.
  2. The app state is updated frequently over time
  3. The Logic to update the state may be complex
  4. The app has a medium or large sized codebase and might be worked with a lot of people.

Redux Terms and Concepts

State Management

lets look at a small react counter component.

It tracks a number in component state and increments the number when a button is clicked

const counter = () => {// state: a counter valueconst [counter, setCounter] = useState(0)// Action: code that causes a change to the state when something happensconst increment = () => {setCounter(prevCounter => prevCounter + 1)}// view : the UI definitionreturn (<div>value: {counter} <button onClick={increment}>Increment</button></div>)}

1. It is a self contained app with the following parts:

The **state** the source of truth that drives our app;

The **view**, a declarative description of the UI based on the current state

The **actions** The events that occur in the app based on user input, and triggers updates in the state

2. This is a small example of one way data flow

State describes the condition of the app at a specific point and time

The UI is rendered based on the state

when something happens (such as user clicking the button), The state is updated based on what occured

The UI re-renders based on the new state

Actions => state => View

However simplicity can breakdown when we have multiple components that need to share the same state, especially if those components are located in different parts of the application. Sometimes this can be solved by **lifting state up** to parent components, but that doesn’t always help.

One way to solve this is to extract the shared state from the components and put it into a centralized location outside the component tree. With this, our component tree becomes a big “view”, and any component can access the state or trigger actions, no matter where they are in the tree!

By defining and separating the concepts involved in state management and enforcing rules that maintain independence between views and state, we give our code more structure and maintainability.

This is the basic idea behind Redux: a single centralized place to contain the global state in your application, and specific patterns to follow when updating that state to make the code predicatable.

## Immutability

Mutable means changeable. If something is immutable it can never be changed.

JavaScript Objects and arrays are all mutable by default. If I create an object i can change the contents of its fields. If I create an array, I can change the contents as well.

const obj = {a: 1, b: 2 }obj.b = 3const array = [‘a’,’b’]array.push(‘c’)arr[2] = c

This is called mutating the object or array. It’s the same object or array reference in memory but the contents inside the object have been changed.

**in order to update values immutably your code must make copies of existing objects/arrays and then modify the copies**

We can do this by using JavaScript **spread operators**, as well as array methods that return new copies of the array instead of mutating the original array.

const obj = {a: {// To safely update obj.a.c, we have to copy each piecec: 3},b: 2}const obj2 = {//copy obj…obj,a: {// copy obj.a…obj.a,//ovewrite cc: 42},b: 2}const arr = [‘a’,’b’]// create a new copy of arr, with ‘c’ appended to the endconst arr2 = arr.concat(‘c’)// make a copy of the original arrayconst arr3 = arr.slice()// and mutate the copyarr3.push(‘c’)

**Redux expects that all state updates are done immutably**

## Terminology

There are some important redux terms that you will need to be familiar with

**Actions**

An action is a plain JavaScipt object that has a type field. You can think of an action as an event that describes something that happened in the application.

The type field should be a string that gives this action a descriptive name, like “todos/todoAdded”

we normally write that type string like “domain/eventName”, where the first part is the feature or category that this action belongs to, and the second part is the specific thing that happened.

An action can have other fields with additional information about what happened. By convention, we put that information in a field called **payload**

A typical action object might look like this

const addTodoAction = {type: ‘todos/todoAdded’,payload: ‘Buy milk’}

**Action Creators**

An action creator is a function that creates and returns an action object. We typically use these so we don’t have to write the action object by hand all the time.

const addTodo = (text) => {return {type: ‘todos/todoAdded’,payload: text}}

**Reducers**

A reducer is a function that recieves the current state and an action object, decides how to update the state if necessary, and returns the new state: (state, action) => newState. **You can think of a reducer as an event listener which handles events based on the received action (event) type**.

## Reducers must always follow some specific rules

1. They should only calculate the new state value based on the state and action arguments.

2. They are not allowed to modify the existing state. Instead they must make immutable updates, by copying the existing state and making changes to the copied values.

3. They must not do any asynchronous logic, calculate random values or cause other side effects.

The logic inside reducers functions typically follows the same series of steps

1. Check to see if the reducer cares about this action

- if so, make a copy of the state, update the copy with new values and return it

2. Otherwise return the existing state unchanged.

Example of a reducer

const initialState = {value: 0}function counterRedcuer(state = initialState, action){switch(action.type){case “counter/increment”:return {…state,value: state.value + 1}default:return state;}}

Reducers can use any kind of logic inside to decide what the new state should be: if/else, switch, loops and so on.

**Store**

The current Redux application state lives in an object called the store

The store is created by passing in a reducer, and has a method called getState that returns the current state value.

import {configureStore} from ‘@reduxjs/toolkit’const store = configureStore({reducer: counterReducer})console.log(store.getState())// {value: 0}

**Dispatch**

The redux store has a method called dispatch. **The only way to update the state is to call store.dispatch() and pass in an action object**. The store will run its reducer function and save the new state value inside and we can call getState() to retrieve the updated value.

store.dispatch({type: ‘counter/increment’})console.log(store.getState())// {value: 1}

**You can think of dispatching actions as “triggering an event” in the application**. Something happened and we want the store to know about it. Reducers act like event listeners, and when they hear an action they are interested in, they update the state in response.

we typically call action creators to dispatch the right action

const increment = () => {return {type: ‘counter/increment’,}}store.dispatch(increment())console.log(store.getState())// {value: 2}

**Selectors**

Selectors are functions that know how to extract specific pieces of information from a store state value. As an application grows bigger, this can help avoid repeating logic as different parts of the app need to read the same data:

const selectCounterValue = state => state.valueconst currentValue = selectCounterValue(store.getState())console.log(currentValue)

Redux application Data Flow

Earlier, we talked about “one-way data flow”, which describes this sequence of steps to update the app:

1. State describes the condition of the app at a specific point in time

2. The UI is rendered based on that state

3. When something happens (such as a user clicking a button), the state is updated based on what occurred

4. The UI re-renders based on the new state

For Redux specifically, we can break these steps into more detail:

1. Initial setup:

- A Redux store is created using a root reducer function

- The store calls the root reducer once, and saves the return value as its initial state

- When the UI is first rendered, UI components access the current state of the Redux store- , and use that data to decide what to render. They also subscribe to any future store updates so they can know if the state has changed.

2. Updates:

- Something happens in the app, such as a user clicking a button

- The app code dispatches an action to the Redux store, like dispatch({type: ‘counter/increment’})

- The store runs the reducer function again with the previous state and the current action, and saves the return value as the new state

- The store notifies all parts of the UI that are subscribed that the store has been updated

- Each UI component that needs data from the store checks to see if the parts of the state they need have changed.

- Each component that sees its data has changed forces a re-render with the new data, so it can update what’s shown on the screen

References : https://redux-toolkit.js.org/introduction/getting-started

--

--