DEV Community

Cover image for Redux explained from a beginner's perspective (simplified)
Ivad Yves HABIMANA
Ivad Yves HABIMANA

Posted on • Updated on

Redux explained from a beginner's perspective (simplified)

Redux is one of confusing concepts that can be very challenging for a beginner getting started using it. From its fancy terminology like reducers, dispatch, payload, to its additional packages like Redux-saga, Redux-thunk, Redux-promise... one may struggle even finding where to start and easily get lost with these Redux Mumbo jumbo. In this guide, we will explain the basics of Redux from a beginner's perspective using simple plain human language. By the end of the article you will understand the basics of Redux and we will use our Redux knowledge to build a simple banking app.

Note: This article will use the traditional way of using Redux (without using redux-toolkit). I intentionally made it this way to focus on explaining redux concepts with less confusing code. But In the following articles in this series, we will integrate redux-toolkit in our code and see precisely problems that the redux-toolkit was created to solve.

Prerequisites

  • We will be building everything from scratch; you only need VS code and node installed in your machine

Let's start by explaining some of the Redux terminologies
1. What exactly is Redux
When you visit the official Redux Website, you see this simple definition ==> Redux:

A Predictable State Container for JS Apps

But what does this even mean? How did they manage to make 7 words so difficult to understand?

First, what is the application state?
Generally, the state of your application is the situation or environment that your app is running in, and this state usually changes. For example, suppose you have a website like Facebook, when you just land on the website, we can say that the application is in the state where no user is logged in, but as soon as you log in, the state changes and now the app is the state where someone is logged in.

Let's take another example where someone visit a website and decide to use the dark mode by clicking it's button as soon as they switch mode everything will change on that page and we can say that the app was in state of light mode and is now in the state of dark mode.

In applications those changes are our state thing like is the user logged in?, is the page loading?, letters you are writing determines the state of our app and We need to track these changes if this state changes, right? For example, Facebook needs to know that someone is logged in so that they can be presented with their messages, and that's where Redux comes in as 'a container of application state.

Basically the state of our application is a long object containing all these information that changes it can look like

let state = {
  userLoggedIn: true,
  mode: "dark",
}
Enter fullscreen mode Exit fullscreen mode

Like now we can see that the user is logged in and chose to use the dark mode. This object will be managed by Redux and track all changes of our app state we can access the state from Redux whenever we want.

so the definition that Redux is "A Predictable State Container for JS Apps" means that Redux is so good at managing your application state that whenever the state in your JS application expect that it is recorded by Redux

2. Redux actions
In Redux actions are much similar real life actions, they describe how to do something. like the way you can have an action of Reading a book that's the same with Redux except that in Redux we are dealing with application state. As we saw that we constantly need to change our application state we need a way to tell Redux how to change the state, and that's where we use actions
In Redux, simply actions are JavaScript objects that explains the action to do on our state. for example an action will look like this

const action1 = {
type: "DO_SOMETHING"
}
Enter fullscreen mode Exit fullscreen mode
  • Redux actions will always have a field of type which describe what to do. and this field is compulsory. note that by convention actions type is written in capital letter separated with underscores
  • Redux action can also have a field of payload which can be anything that give more detail about how to do the action but this field is optional.

Let's use an example. Let's say in real life you want to go to Simba supermarket to buy 10 red apples we can describe this as a Redux action like the Following

const action = {
  type: 'BUY_APPLES',
  payload: {
    shop: 'Simba supermarket',
    type: 'red',
    number: 10,
  }
};

Enter fullscreen mode Exit fullscreen mode

In this example our action is just an object containing the type describing that we want to buy apples and in our case the payload is another object containing additional information of where to buy apples which type and how much to buy

for a more practical example let's in a TODO app you want to create an action that will add a new TODO to the state of TODOS list in our application the action could be like

const action = {
type: "ADD_TODO",
payload: "My first ToDo"
}
Enter fullscreen mode Exit fullscreen mode
  • Note how the payload can also be just a string as I don't have additional information to explain this action

Redux action creators
In Redux as the name says action creators are functions that create actions but you may say that we already created action in the previous why would we need to use action creators? let's take the apples example mentioned above what if we wanted to buy 7 green apples from a different shop. the action is still the same we don't need create a new one we can just use a function that take input and return appropriate action
we can use something like

const createAction = (shopName, appleType, appleNumber) => {
  return {
    type: 'BUY_APPLES',
    payload: {
      shop: shopName,
      type: appleType,
      number: appleNumber,
    },
  };
};
Enter fullscreen mode Exit fullscreen mode

action creators are function that just return the action whenever we want this action we can call that function customize those parameters and the action will be created for us

3. Redux Reducers
So we have a state and we have an action we want to do to the state how exactly do we tell redux to do the action and change the state. that's where "reducers" come in. Redux use fancy names so what in the world is a reducer? By definition

A Reducer is a pure function that takes the state of an application and action as arguments and returns a new state

Pure functions? Pure functions are functions that will always return the same output whenever they are given the same arguments. but isn't that what all function do? returning same results? well, not really
consider these functions

const functionA = (number)=>{
  const sum = number + 2;
  return sum;
};

let x = 2
const functionB = (number)=>{
  const sum = number + x;
  return sum;
}
Enter fullscreen mode Exit fullscreen mode

these two functions may look like they are doing the same but functionA is a pure function while functionB is an impure function. This is because functionA will always return the sum when a same number is passed but functionB is depending on variable x and if this value is changed functionB will return a different sum.

There is more to pure functions, I recommend that you read these to articles to understand article1 article2

Back to the definition of a Reducer

A Reducer is a pure function that takes the state of an application and action as arguments and returns a new state

A reducer is just a function that will take the initial state and the action we want to do and return a new changed state. so a typical reducer will look something like

const reducer = (state, action)=>{
  // do action ...
  return newState
}
Enter fullscreen mode Exit fullscreen mode

let use an example of counter where can start from zero and increment or decrement the number by one

we can have our initial state of 0 as value

const state = {
 value: 0
}
Enter fullscreen mode Exit fullscreen mode

we can have our actions like

const incrementAction = {
type: 'INCREMENT'
}

const decrementAction = {
type: 'INCREMENT'
}
Enter fullscreen mode Exit fullscreen mode

Let's now create a reducer that will take a state and an action to return a new state. when we pass the increment action we will increase the current current state by 1 and when we pass decrement action we decrement it by 1
we will use switch statements to check which action we have

const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT': {
      const newState = { ...state };
      newState.value = state.value + 1;
      return newState;
    }
    case 'DECREMENT': {
      const newState = { ...state };
      newState.value = state.value - 1;
      return newState;
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

Let's break this line by line

  • const reducer = (state, action): We are creating a reducer function that take initial state and action object as the definition of a reducer says
  • switch (action.type) As we have two actions we are using the switch statement to check the type of the action. You can also use if else statements if you want to
  • const newState = { ...state }: this is the most important part a reducer- is a pure function and will NEVER mutate the state passed to it as argument instead we create a new object and copy the previous state using spread operator. we are just creating a new object and copy everything from the state this means that newState and state are different objects.
  • newState.value = state.value + 1: We are changing the value field of newState to be the previous state value incremented or decremented by one according to the action type
  • return newState: we are returning new state as a reducer should return new state

the above code can be simplified to be

const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, value: state.value + 1 };
    case 'DECREMENT':
      return { ...state, value: state.value - 1 };
  }
};
Enter fullscreen mode Exit fullscreen mode

4. Redux store
Now we have a state, we have actions that describes what to do with the state, we have a reducer function that implement our action and return the new state. It looks like we have everything we need we just need a better management of all of these code.

Basically we want that when we call the reducer function and return the new state, this new state should replace the old state and be our current state so the next time we do something we have a track of how the state changed in our app. To achieve this we need to have everything in the same place which is where the Redux store comes in.
The Redux store is like a real life store that holds records of how the state had changed in your application. for example when a user login the state changes, when they log out the state will change again Redux store will keep a track of these changes so if anything go wrong we can see exactly what happened and where it happened.

in Redux to access the store we need to create it first, the store is created using the function createStore() and this function provide us functions that we will use to access and manipulate the state
In this guide we will be focusing on it's two functiongetState() and dispatch()

  • getState(): when you run this function the store will return the current state.
  • dispatch(): In Redux you don't really call the reducer and pass the action directly as we did before, instead you pass the action to this dispatch function of the store, and the store will will have your reducer and the sate and do everything for you.

this means that you don't need to worry about what's in the state you just dispatch (send) an action to the store, the store will call the reducer and pass the state and the action you dispatched. the reducer will do it's work as we saw earlier and when it returns the new state the store will automatically update the state to this new state.

It's like the way you go to the bank and you have an action of depositing money to your account the cashier will take your money do her work and add new amount to your account. With no effort done on your end

Don't worry if you don't understand all we said about Redux store let's see all in action as we build our simple banking app

PUTTING EVERYTHING TOGETHER: SIMPLE BANKING APP

Let's use what we learned to build a simple banking app where someone create accounts, view their balance, deposit or withdraw money from their account

follow these steps

1. Create a project
create a folder and open it in VS Code initialize a node project by running

npm init -y
Enter fullscreen mode Exit fullscreen mode
  • in the package.json add a type field and set it value to "module" as we will be using imports and export later

2. Install Redux

  • install Redux by running the following command
npm install redux

// using yarn

yarn add redux
Enter fullscreen mode Exit fullscreen mode

3. Create a redux store

  • create a folder called redux and that's where our redux code will be
  • in the redux folder create a file and name it store.js this where we will configure our Redux store
  • in the 'store.js' file add the following code
import { legacy_createStore as createStore } from 'redux';

const store = createStore();
Enter fullscreen mode Exit fullscreen mode

we are importing createStore from redux and we are creating a new store by invoking that function the createStore function

4. Create a initial state
Let's have the initial state of our application let's say someone just created a new bank account and their basic information will be our object

  • in the store.js right before we create the store variable we will add a variable of the initial state and will pass our initial state into the store so that it will store it for us the store.js should look like
import { legacy_createStore as createStore } from 'redux';

const initialState = {
  accountOwner: 'John Doe',
  address: 'Miami',
  balance: 0,
};
const store = createStore(initialState);

export default store;
Enter fullscreen mode Exit fullscreen mode
  • we are creating an initial state that includes basic information of the owner and their balance is 0$ as they just created a new account and they don't have money yet.

5. Create action using action creator
Remember the actions and action creators we talked about earlier right? actions are object and action creators are function that return these objects

  • in the redux folder create file called actions.js and we will add our action creators
  • let's create an action for Depositing money , withdrawing money, and changing address

in your actions.js add the following code

export const depositAction = (amount) => {
  return {
    type: 'DEPOSIT',
    payload: amount,
  };
};

export const withdrawAction = (amount) => {
  return {
    type: 'DEPOSIT',
    payload: amount,
  };
};

export const changeAdressAction = (newAdress) => {
  return {
    type: 'CHANGE_ADRESS',
    payload: newAdress,
  };
};

Enter fullscreen mode Exit fullscreen mode

we are creating action creator functions that just return action with type and the payload the value we passed in
for instance the depositAction will return an action with type DEPOSIT and a payload of the amount you passed in.

6. Create a reducer
in the redux folder create a reducer.js file which will contain our reducer

  • in the reducer.js add the following code
const reducer = (state, action) => {
  switch (action.type) {
    case 'DEPOSIT':
      return { ...state, balance: state.balance + action.payload };
    case 'WITHDRAW':
      return { ...state, balance: state.balance - action.payload };
    case 'CHANGE_ADRESS':
      return { ...state, address: action.payload };
    default:
      return state;
  }
};
export default reducer;
Enter fullscreen mode Exit fullscreen mode
  • As always it's important that the reducer doesn't mutate the state passed. We create a new object and copy everything in the previous state and change the field we want to change
  • in this case when the action is DEPOSIT we will change the balance to add amount in the payload to previous balance. the same thing with WITHDRAW instead we subtract amount in the payload from previous balance
  • when the action is CHANGE_ADRESS we will only change the address field to the new address from the payload
  • If the action is not known by default we will do nothing we just return previous state unchanged

7. Pass the reducer to the store
Remember that we don't have to do anything ourselves the redux store will do everything for us therefore we need to provide the reducer to the store.

  • back to the store.js import the reducer function and pass it to the createStore function.
import { legacy_createStore as createStore } from 'redux';
import reducer from './reducer.js';

const initialState = {
  accountOwner: 'John Doe',
  address: 'Miami',
  balance: 0,
};
const store = createStore(reducer, initialState);

export default store;

Enter fullscreen mode Exit fullscreen mode
  • we are importing reducer function from reducer.js and pass it to the createStore function along with the initial state we had before Note that the reducer should be passed first as createStore function expects the reducer to be the first argument

That's all configurations we need now lets test how everything works

8. Testing

in the root folder create an index.js file and import the store and actions from redux folder.

  • in the index.js add the following code
import {
  changeAdressAction,
  depositAction,
  withdrawAction,
} from './redux/actions.js';
import store from './redux/store.js';

console.log('initialState:');
console.log(store.getState());
//
store.dispatch(depositAction(500));
console.log('New state after deposit:');
console.log(store.getState());
//
store.dispatch(changeAdressAction('Paris'));
console.log('New state after change address');
console.log(store.getState());
//
store.dispatch(withdrawAction(300));
console.log('New state after withdraw');
console.log(store.getState());

Enter fullscreen mode Exit fullscreen mode
  • to test everything we are just consoling the state by using store.getState() remember that getState returns our current state
  • we are dispatching actions by using store.dispatch() and we pass in the function we want to dispatch
  • after dispatching an action we are consoling the state again to see changes

  • Run node index.js in the terminal and you should see the following output

terminal output

  • you can see that after dispatching an action redux did updated our state

There you have it! you now understands the basics of Redux In the following article in this series we will look into how to use Redux-toolkit to write cleaner code and integrate Redux in a real redux app that is more interactive.

For reference you can find code mentioned in this article on this github repo

Discussion (0)