Beginning With Redux: Part-I (Basic Fundamentals)

Faiz Bachoo Shah
10 min readJan 29, 2021
Redux Logo
Redux Logo

If you have worked with React, chances are you might have already heard of the term Redux. But, what is Redux, and what is it used for? Well if this question hangs on your mind, or if you’re just a curious developer who wants to know a bit about Redux, then this blog is for you.

Before reading this blog, you should have some pre-requisite knowledge about the following topics:

  • React
  • JSX
  • Use of Components
  • State & Props in React

In this blog, we are going to learn what is Redux and its uses, as well as how to use it in a live project by making a small hands-on React website using Redux(A simple Counter app). But first, we are going to learn the fundamentals of Redux, such as Reducers, Actions, Store, etc. as having the fundamentals clear is important to master any topic.

The blog has been divided into the following sections:

  • Introduction
  • Why Redux?
  • Fundamentals of Redux

So let’s dive into our Redux tutorial!!

Introduction

According to the official Redux website, the definition of Redux is given as follows:

Redux is a pattern and library for managing and updating application state, using events called “actions”. It serves as a centralized store for state that needs to be used across your entire application, with rules ensuring that the state can only be updated in a predictable fashion.

Let’s understand it in simpler words.

Redux is basically a pattern and library(Although its more closer to a data-flow architecture rather than a traditional library) which helps you to manage the global state of an entire application, and updates the global state using events called “actions”. We know how in React, every component has its own internal state. But more often than not, we come across situations where we want to share the state of a component with some other component.

For example, in a social media website, every post has a share button. Each post is a separate component, with its own state containing the details of the post which it got by an API request from the server. Now, it might happen that on clicking the “Share” button, before the actual share function occurs, a small pop-up screen occurs which shows the details of the post and with “Confirm Share” button. Now, mind it, this Pop-up screen might not be a child component of the Post component. In fact, its more likely to be a separate component of its own. So how are you going to share the details of the Post component with the pop-up component ? One way is probably to make another API request to the server to get the post details again, but that will be an inefficient practice.

A better way would be if somehow the post details could have been stored in a global state, where any component of React app could access it at any time. This is exactly what Redux does, as it creates a global state and helps in managing it.

Redux is basically an application state manager which maintains the state of an entire application in a single immutable tree(A JavaScript object). Immutable means that state can’t be changed directly. The only way to change the global state is through events called actions and functions called reducers which creates a new global state and replaces the previous global state.

Why Redux ?

Redux is not the only state-management library for React. There are other libraries such as Flux and Context, so why Redux? Well, here are some of the reasons why you should consider Redux:

Predictability

Redux always keeps the state predictable. It helps in keeping the behaviour of the entire application consistent. It means, at any point of time in your application, if you pass the same state and action to a reducer, it will perform the same task and produce the same result as expected. This is possible because the reducers are pure functions(a function which given the same input, will always return the same output without any side effects), and all the syncing and updating happens through the same source of truth, the store.

Maintainability

Redux has a strict structure in code organization, which means anybody with knowledge of Redux could understand the structure and code of any Redux application. This in turn helps in making the code easier to maintain.

Debuggable

The Redux DevTools makes it easy to debug your application by tracking all the details about your application’s state in real time, from actions to state changes. It even lets you log changes and thus makes it easy to understand coding errors, network errors, and other form of bugs that might come up.

Server-side Rendering

Redux could also be used for server-side rendering. This is especially useful during the initial render, where you can send the state of an app to the server along with its response to the server request. The required components are then rendered in HTML and sent to the client-side.

Ease of Testing

The testing becomes very easy in Redux, as most of the Redux code are pure, small, isolated functions which does only a single task.

Fundamentals of Redux

Before we could dive into code and start making projects, we first need to understand how Redux works. Redux projects have three building parts: Actions, Reducers & Store. Let’s understand each one of them.

Actions

In its simplest term, actions are events. Actions are basically plain JavaScript objects which you send to the Redux store to tell the store about the type of event that has occurred, and any data which you want to send to the store related to that event. An event in Redux context is anything which tends to change the global state of the app. For example, in a Social Media website, logging in, logging out, uploading a post, deleting a post, etc. could all be a list of different events.

Actions consists of two fields: a mandatory “type” field, which is usually a String constant describing the type of event, and an optional “payload” field which contains the data related to that event. This is how an action corresponding to posting a new post in a social media website might look like:

{
type: "NEW_POST_SUBMIT",
payload: {
username: "John Doe",
postContent: "Today is a nice day!!!"
}
}

The “type” field is how store understands, with the help of reducers, that what type of an action has just taken place. In this example, the action will tell the store through its “type” field that an event of new post submission has occurred. The “payload” field contains the actual data related to the new post, an object which contains the username of the post submitter, as well as the actual body-content of the post.

An action is sent to the store whenever we want to change the global state of the entire application. You could think of it as a stimulus which informs the store that “Hey, an event just occurred which might require a change in the global state of the application. I’m informing you about the type of event which occurred, as well as the data corresponding to the event. Now you handle the rest”. Remember, actions only informs the store about the type of event that occurred as well as the data related to it. They don’t decide what the store should do with that information, i.e. they don’t decide how the global state should be updated when that event occurs. That is the job of a reducer.

Remember, actions only informs the store about the type of event that occurred as well as the data related to it. They don’t decide what the store should do with that information, i.e. they don’t decide how the global state should be updated when that event occurs. That is the job of a reducer.

Actions are usually created with the help of Action Creators. Action Creators are just JavaScript functions which creates and returns an action object. An action creator corresponding to the new post action might look like this:

// Here newPost is a JS object which contains
// the details of the post.
// newPost = {
// username: "John Doe",
// postContent: "Today is a nice day!!!"
// }
const addNewPost = (newPost) => {
return {
type: "NEW_POST_SUBMIT",
payload: newPost
}
}

Action creators are usually used so that we don’t need to write the action object by hand every time we want to create a new action. Using action creators saves time when we are writing code.

Till now, we have just created the action. We need to send this action to the store so that Redux gets to know that an event has occurred. We can do this by calling the dispatch() method of the Redux store object(We’ll be covering this soon) called store. It will look like this:

store.dispatch(addNewPost(newPost))

Here, newPost is a plain JS object containing the details of the post. When we call the addNewPost() function, it returns an action. Using dispatch() method of the store object, we are sending the action created by the addNewPost() to the store. Here is a detailed example of creating and sending an action to the store:

// Creating the new post object
const newPost = {
username: "John Doe",
postContent: "Today is a nice day!!!"
}
// Creating the Action Creator
const addNewPost = (newPost) => {
return {
type: "NEW_POST_SUBMIT",
payload: newPost
}
}
// Assuming we already have a Redux "store" object
// initialized somewhere, call the dispatch method
// of the "store" to send the action created by the
// action creator addNewPost to the Redux store
store.dispatch(addNewPost(newPost))

Reducers

A reducer is a pure function which takes in two parameters, the current application state and an action, decides how to update the state if necessary, and returns a new state.

They are the main building part of the Redux architecture which decides how to update the application state whenever a particular type of event occurs. If actions tell the store about what type of event has occurred, reducers use that information sent by the actions to decide how to update the application state.

Being pure functions, they do not cause any side-effects to the application, and given the same state and action as parameters, will return the same result.

Here is how a typical reducer looks like:

// A plain JS object which acts as the initial state
// for the reducer
const initialState = {
postDetails: null
}
// The actual reducer function
const postReducer = (state = initialState, action) {
// Checking the type of action
switch (action.type) {
case "NEW_POST_SUBMIT":
// If the type of action
// is a new post creation,
// return a new object containing
// the details about the post
// sent by the action's payload
return {
...state,
postDetails: action.payload
}
case "DELETE_POST":
// If the type of action
// is to delete the post,
// return a new object
// which sets the postDetails
// to null
return {
...state,
postDetails: null
}
// Default case, returning the
// same state unchanged
default:
return state
}
}

In this example, the reducer postReducer first checks what type of action has been passed to it, and then updates the state object passed to it accordingly. If the type is “NEW_POST_SUBMIT”, it will update the state with the post details sent by the action’s payload. If its “DELETE_POST”, it’ll set the the postDetails field of the state object to null. Else it’ll return the same state unchanged.

You might have probably noticed that you could use multiple actions for the same reducer. In practice, we don’t want to put all our actions through the same reducer, as that doesn’t make the code modular. For example, you probably don’t want actions which deals with authentication functionalities such as signing up, logging in and logging out to be using the same reducer used by the actions that deals with post creation and deletion. You probably want two separate reducers for them — authReducer and postReducer.

But here comes a catch. Creating two different reducers also means creating two different initial state, one for each of the two reducers. Now, Redux only supports a single state tree, which means you can only have a single global application state. So to tackle this problem, a function called combineReducers() provided by Redux could be used. It combines all the reducers into a single index reducer. Every reducer in it is responsible for its part of the app’s state, with every reducer having its own state parameter. For example, the authReducer will only handle the auth actions and will only update the state which is associated with the authReducer. Same happens with the postReducer, as it only handles the post actions and only updates the state associated with the postReducer.

Here is an example of combineReducers() at play:

const rootReducer = combineReducers({
auth: authReducer,
post: postReducer
});

By doing this, the rootReducer will become the main reducer for the Redux store, and the global application state will become a combination of individual state objects associated with each separate reducer. To access the state object of each separate reducer, you need to access them by the key you passed for the corresponding reducer in the combineReducer function. In the above example, to access the state objects of authReducer and postReducer, you need to use the auth and post keys, respectively.

Store

Store is the object which holds the application state. It is the object which provides helper methods to access the state, dispatch actions and register and unregister listeners. It is highly recommended to have only one store in your application.

As you might have noticed that actions and reducers are plain JS functions, and store is how you connect it to Redux. To create a store object, you need to use the createStore() function of Redux. It takes in three parameters:

  • The root reducer of the application.
  • The initial state of the application.
  • Any middlewares which you want to apply to the store.

Here is an example of the creation of a store object using createStore() :

import { createStore } from 'redux';const initialState = {}const store = createStore(rootReducer, initialState, applyMiddleware());

With this, we have finally come to an end with the theory part of Redux. In the next blog, we will be making a small, simple Counter App based on the lessons we learnt in this blog.

--

--

Faiz Bachoo Shah

The best thing about today is that its not yesterday.