loading...

explain Redux like I'm five | Redux Cheatsheet

rahuldkjain profile image Rahul Jain ・6 min read

This post will help you to revise/learn basic Redux concepts with an amazing real-world example. So let's get started!

Redux is a predictable state container for JavaScript apps.

The term predictable in this context means, that using Redux you'll know what every single action in the application will do and how the state will change.

There are basically three actors in redux app i.e. store, actions, and reducers.

Everything in redux is unidirectional i.e.
state -> action -> reducer -> state -> action -> reducer -> state ...

It helps you write applications that behave consistently.

Let's understand these actors with an example of the banking process.

Store

Let's say you want to withdraw money from your bank account. You will go to the bank to do that. That bank will have the vault, where they keep all the money.

The Bank Vault is to the Bank what the Redux Store is to Redux.

Have a single source of truth: The state of your whole application is stored in an object tree within a single Redux store.

Wait, what is state by the way?
State is nothing but the value that is managed by the store. The money is to the bank what the state is to store.

In simple terms, with Redux, it is advisable to store your application state in a single object managed by the Redux store.

One application STATE OBJECT managed by ONE STORE

State is read-only: The only way to change the state is to emit an action, an object describing what happened.

Action

Actions define your intention.
To withdraw money, you need to convey your intention to the cashier i.e. to withdraw money.

{ 
  type: "WITHDRAW_MONEY",
  amount: "$5,000"
}

The only way to change the state is to emit an action, on object describing what happened.

Reducer

The cashier is to the bank what the reducer is to Redux.

If you want to update the state of your application, you convey your action to the reducer. This process is mostly called dispatching an action.

The reducer knows what to do. In this example, it will take your action to WITHDRAW_MONEY and ensure that you get your money.

In Redux terms, the money you spend is your state. So, your reducer knows what to do, and it always returns your new state.

To specify how the state tree is transformed by actions, you write pure reducers.

With this analogy, you should now have an idea of what the most important Redux actors are: the store, the reducer, and an action.

Here’s what the code to create a Redux store looks like:

import { createStore } from "redux"; //an import from the redux library
const store = createStore();  // an incomplete solution - for now.

The REDUCER always “talks” to the STORE. The Store and the Reducer are great buddies. Always in sync.

The Reducer is the only mandatory argument passed into createStore()

Well, according to the Redux official docs:
Reducers are the most important concept in Redux.
A reducer is also called a reducing function you probably already use a reducer — if you’re conversant with the Array.reduce() method.

let arr = [1,2,3,4,5]
let sum = arr.reduce((x,y) => x + y)
console.log(sum)  //15

The functions passed into arr.reduce is called a reducer.
In this example, the reducer takes in two values, an accumulator, and a currentValue, where x is the accumulator, and y is the currentValue.

In the same manner, the Redux Reducer is just a function. A function that takes in two parameters. The first being the STATE of the app, and the other the ACTION.

But where does the STATE and ACTION passed into the REDUCER come from?
The Array.reduce method is responsible for passing in the needed arguments, x and y into the function argument, the reducer. So, the arguments didn’t come out of thin air.

The Redux reducer is also passed into The createStore factory function. Like Array.reduce(), createStore() is responsible for passing the arguments into the reducer. A reducer always returns something. In the initial Array.reduce() reducer example, we returned the sum of the accumulator and current value. For a Redux reducer, you always return the new state of your application.

initialState is the second argument passed into the createStore function call.

Whenever you create a store with createStore(), the created store has three exposed methods.
getState() - store.getState() will return the object
dispatch() - store.dispatch(action) will dispatch an action
subscribe() - to subscribe to watch the change/update of state.

To handle the actions passed into the reducer, you typically write a switch statement within your reducer
It will switch over the action type and do something based on the type of action passed in.

The action-type is all written in capital letters. It’s not compulsory, but it’s a pretty popular style in the Redux community.

For one, they all have the same type field. If we had to dispatch these actions in multiple places, we’d have to duplicate them all over the place. That’s not so good. Especially because it’s a good idea to keep your code DRY.
Welcome, Action Creators.

Action Creators are simply functions that help you create actions. That’s all. They are functions that return action objects.

export function withdrawMoney (amount) {
  return {
     type: "WITHDRAW_MONEY",
     amount: amount
   }
}

It is a common practice to have the major actors of a redux app live within their own folder/directory. By actors, I mean, the reducer, actions, and store.

For big applications, though, this is certainly a pretty decent practice.
In each of the folders, create an index.js file. This will be the entry point for each of the Redux actors (reducers, store, and actions).

The actions, when dispatched, flow through the reducer.

Never Mutate State Within the Reducers
You should not mutate the state received in your Reducer. Instead, you should always return a new copy of the state.

Right Way

export default (state, action) => {
  switch (action.type) {
    case "WITHDRAW_MONEY":
      return {
        ...state,
        amount: action.amount
      };

    default:
      return state;
  }
};

Wrong Way

export default (state, action) => {
  switch (action.type) {
    case "WITHDRAW_MONEY":
      state.amount = action.amount; 
      return state;

    default:
      return state;
  }
};

Thanks to the ES6 spread operator, ...state. However, the tech field is updated to what comes in from the action, action.text

The Redux store, whatever store you create has a subscribe method called like this: store.subscribe().

The argument passed into store.subscribe() is a function, and it will be invoked whenever there’s a state update.

// in app.js

const render = () => ReactDOM.render(<App />, document.getElementById("root"));

render();
// Any time there’s a successful update to the store, the <App/> will now be re-rendered with the new state values.
store.subscribe(render);

Containers & Components

In Redux applications, it is a common pattern to split your components into two different directories.

Every component that talks directly to Redux, whether that is to retrieve state from the store, or to dispatch an action, should be moved to a containers directory.

Other components, those that do not talk to Redux, should be moved over to a components directory.

Reducer Composition(Multiple Reducers)

It is common to have multiple reducers in your application as opposed to one reducer handling all the operations of the state. These reducers are then combined into one.

Reducer composition requires that a single reducer handles the state update for a single field in the state object.
One more important thing to point out is that the return value from each of the reducers is solely for the field they represent.

To combine these multiple reducers, we need the helper function combineReducers from redux.
An important point to always remember is that when using combineReducers, the value returned from each reducer is not the state of the application.

It is only the value of the particular key they represent in the state object!

Actions in constants

create a new actions directory. While at it, also create a constants folder.

In the constants folder, create a new file, action-types.js.

This file has the sole responsibility of keeping the action type constants.

Summary

  • It is a good practice to always plan your application development process before jumping into the code.
  • In your state object, avoid nested entities at all costs. Keep the state object normalized.
  • Storing your state fields as objects do have some advantages. Be equally aware of the issues with using objects, mainly the lack of order.
  • The lodash utility library comes very handily if you choose to use objects over arrays within your state object.
  • No matter how little, always take some time to design the state object of your application.
  • With Redux, you don’t always have to pass down props. You can access state values directly from the store.
  • Always keep a neat folder structure in your Redux apps, like having all major Redux actors in their own folders. Apart from the neat overall code structure, this makes it easier for other people to collaborate on your project as they are likely conversant with the same folder structure.
  • Reducer composition is really great especially as your app grows. This increases testability and reduces the tendency for hard-to-track errors.
  • For reducer composition, make use of combineReducers from the redux library.
  • The object passed into the combineReducers function is designed to resemble the state of your application, with each value gotten from the associated reducers.
  • Always break larger components into smaller manageable bits. It’s a lot easier to build your way up that way.

Reference: https://www.freecodecamp.org/news/understanding-redux-the-worlds-easiest-guide-to-beginning-redux-c695f45546f6/

Posted on by:

rahuldkjain profile

Rahul Jain

@rahuldkjain

♥️In relationship with front end dev👨‍💻

Discussion

pic
Editor guide
 

Really liked your explanation. Been doing Redux for Angular and React for years and I learnt it the hard way. This will definitely help a beginner who is learning state management.

 

Thanks Dayam!