Redux Demystified
Tags: redux, react, javascript, beginners
Published: 2022-07-08 08:21:03

## What is Redux?

Redux is a predictable container for JavaScript apps.

## Redux is for JavaScript applications

Redux is not tied to React. Can be used with Angular, Vue, or even vanilla JS.

## Redux is a state container

Redux stores the state of your application.
![Different states in a React application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sys2lsgi5b6brknm4428.png)
**The** **state of an application is the state shared by all the individual components of that application.**

Redux will store and manage the application state.

## Redux is predictable

Redux is a state container and in any JavaScript application, the state of the application can change.

![Redux is predictable](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qcek5ii7hc84ybw97ay7.png) 

In Redux, a pattern is enforced to ensure that all the state transitions are explicit and can be tracked.

## Why Redux?

Redux will help you manage the global state of your application in a predictable way.

The patterns and tools provided by Redux make it easier to understand when, where, why & how the state in your application is being updated.

Redux guides you towards writing code that is predictable and testable.

## What is Redux Toolkit?

Redux Toolkit is the official, opinionated, batteries-included toolset for efficient Redux development.

It is also intended to be the standard way to write Redux logic in your application.

## Why Redux Toolkit?

Redux is great, but it has a few shortcomings: 

- Configuring Redux in an app seems complicated.
- In addition to Redux, a lot of other packages have to be installed to get Redux to do something useful.
- Redux requires too much boilerplate code.

**Redux Toolkit** serves as an abstraction over Redux. It hides the difficult parts ensuring you have a good developer experience.

## React-Redux
![React-Redux package](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7vki6edgakkpp3w6ztz.png)

## Summary of what we’ve learned so far

- React is a library to build user interfaces.
- Redux is a library for managing the state in a predictable way in JS apps.
- Redux Toolkit is a library for efficient redux development.
- React-Redux is a library that provides bindings to use React and Redux Toolkit together in an application.

## Caveats

- Never learn React and Redux in parallel.
- “When to use Redux in your application?”
Redux helps you deal with shared state management but like any tool, it has certain trade-offs. 
*Pros* 
- You have **large amounts of application state** that are needed in many places in the app.
- The app **state is updated frequently** over time.
- The logic to **updating that state may be complex**
- The app has a **medium or large-sized codebase** and might be worked on by many people.
*Cons*
- There are **more concepts** to learn and **more code** to write. 
- It also adds some indirections to your code and asks you to follow certain **restrictions**. 
- It’s a **trade-off** between long-term and short-term productivity.

## Prerequisites

React Fundamentals
React Hooks

## Getting Started with Redux

1. Install **node.js** if you haven’t already. Here’s the link [https://nodejs.org/en/](https://nodejs.org/en/)
2. Create a folder `learn-redux` or any other name on your desktop.
3. Open the folder in your code editor, preferably Visual Studio Code.
4. Inside the folder, in your terminal, enter the command `npm init --yes`
This will initialize a `package.json` file with the default settings.
For reference,
`PS E:\GitHub\learn-redux> npm init --yes`
5. Add redux as a dependency for your project. Enter the command `npm-install-redux` in your terminal.
For reference,
`PS E:\GitHub\learn-redux> npm install redux`
6. Create an `index.js` inside your folder.

That’s it! We are all set to get our hands dirty in Redux Toolkit 🚀

## Three Core Concepts

1. A **store** that holds the state of your application.
2. An **action** that describes what happened in the application.
3. A **reducer** is what ties the store and actions together. It handles the action and decides how to update the state.

Let’s consider an example of a Cake Store. 

- A ***store*** is similar to a cake store in the sense that the cake store has a number of cakes in its store inventory. On the other hand, a redux store has its states in its store.
- An ***action*** is when a customer places an order for a cake. In that case, an order has been placed and the count of the cakes has to be reduced by one.
- A ***reducer*** in our case is a shopkeeper. He receives the order from the customer, which is an *action* and removes the cake from the shelf which is a *store*.

## Three Principles

1. First Principle -
***The global state of your application is stored as an object inside a single store.***
In simpler terms, maintain our application state in a single object which would be managed by the Redux store.
2. Second Principle -
***The only way to change the state is to dispatch an action, an object that describes what happened.***
Thus, to update the state of your app, you need to let Redux know about that with an action. One should not directly update the state object.
3. Third Principle -
***To specify how the state tree is updated based on actions, you write pure reducers.***
The reducer takes the previous state and an action and returns a new state.

`Reducer - (previousState, action) ⇒ newState`

Let’s get back to our Cake Shop.

- Let’s assume we are tracking the number of cakes on the shelf. So our object would look something like this.
```jsx
// A redux store as per the First Principle

{
	numberOfCakes: 10
}
```
- A common action would be scanning the QR code to place an order for a cake. This action would look like the one below.

```jsx
// A redux action as per the Second Principle

{
  type: 'CAKE_ORDERED'
}
```

- A reducer could be a shopkeeper in our case. The shopkeeper performs the action of placing an order and then reduces the cake count. Just like this reducer below.

```jsx
const reducer = (state = inititalState, action) => {
  switch (action.type) {
		case CAKE_ORDERED:
			return {
				numberOfCakes: state.numberOfCakes - 1
			}
	}
}
```



### Three Principles Overview  
![Three Principles Overview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mr8mhkvdavxfq24v27di.png)

## Diving deeper into the Three’s

### Actions

- The only way your application can interact with the store.
- Carry some information from your app to the redux store.
- Plain Javascript objects.
- Have a `type` property that describes something that happened in the application.
- The `type` property is typically defined as string constants.
- An *action creator* is a function that returns an object.

### Reducers

- Reducers specify how the app’s state changes in response to the actions sent to the store.
- Reducer is a function that accepts state and action as arguments and returns the next state of the application.
`(previousState, action) ⇒ newState`

### Store

- One store for the entire application.
- Responsibilities of a Redux store:
    - holds the application state
    - allows access to the state via `getState()`
    - allows the state to be updated via `dispatch(action)`
    - registers listeners via `subscribe(listener)`
    - handles unregistering of the listeners via the function returned by `subscribe(listener)`

## Bind Action Creators

The first argument is an object where we define different action creators.
The second argument is what we want to bind those actions to.

```jsx
const bindActionCreators = redux.bindActionCreators()

const actionCreatorOne = (paramOne = 1) => {
	return {
		type: "ACTION_ONE",
		payload: paramOne
	}
}

const actions = bindActionCreators({ actionCreatorOne(), actionCreatorTwo() }, store.dispatch)

actions.actionCreatorOne()
actions.actionCreatorTwo()
```

Although bind action creators are not necessary, redux does bring it along with all of its other packages.

## Combine Reducers

```jsx
const combineReducers = redux.combineReducers

const rootReducer = combineReducers({
	keyOne: // reducerOne,
	keyTwo: // reducerTwo
})

const store = createStore(rootReducer)
```

`combineReducers` take an object as an argument. The object has keys as any name and the values as a reducer function.
When we dispatch an action, both the reducers receive that action. The difference is that one of them acts on the action whereas the other just ignore it.
Now by doing what we have just done, each of the reducers is managing its own part of the application global state.
The state parameter is different for every reducer and corresponds to the part of the state it manages.
When your app grows, you can split the reducers into different files and keep them completely independent, managing different features. For example, authReducer, a userReducer, profileReducer, etc.

## Immer

In a Redux environment, we learned to never mutate the object state.
Here’s how we achieved the same.

```jsx
const cakeReducer = (state = initialCakeState, action) => {
  switch (action.type) {
    case CAKE_ORDERED:
      return {
        ...state, // spread operator to make a copy of all the properties
        numberOfCakes: state.numberOfCakes - 1, // only update the desired property
      };
    case CAKE_RESTOCKED:
      return {
        ...state,
        numberOfCakes: state.numberOfCakes + action.payload,
      };
    default:
      return state;
  }
};
```

In practical applications, the state is more complex with nested levels, and in such situations, updating the state could be troublesome.

Immer simplifies handling immutable data structures.

To install `immer` enter the `npm install immer` command in your terminal.

```jsx
const personalData = {
	name: "Vedant",
	address: {
		street: "123 Main St",
		city: 'Boston',
		state: 'MA',
	}
}

{
	...personalData,
	address: {
		...personalData.address,
		street: "789 Main St"
	}
}

produce(personalData, (draft) => {
	draft.address.street = "789 Main St"
})
```

## Middleware

It is the suggested way to extend Redux with custom functionality.

Provides a third-party extension point between dispatching an action, and the moment it reaches the reducer.

Middleware is usually used for logging, crashing, reporting, performing asynchronous tasks, etc.

Let’s check out the `logger` middleware. To use `logger` , enter the command `npm i redux-logger` in the terminal.

### redux-logger

Log all information related to redux in your application.

```jsx
const applyMiddleware = redux.applyMiddleware

const reduxLogger = require("redux-logger")
const logger = reduxLogger.createLogger()

const store = createStore(rootReducer, applyMiddleware(logger))
```

## Async Actions

Try recollecting the cake shop scenario again. So, the following were the events occurring in a cake shop.

As soon as an action was dispatched, the state was immediately updated.

Thus, if you dispatch the `CAKE_ORDERED` action, the `numberOfCakes` was right away decremented by 1.

Same with the `ICECREAM_ORDRERED` action as well.
All the above actions were ***synchronous actions***.

***Asynchronous actions*** comprise asynchronous API calls to fetch data from an endpoint and use that data in your application.

### What next?

Let’s have our application fetch a list of users from an API endpoint and store the list in a redux store. We already know that there exists the state, the actions, and the reducers as the three main concepts in any redux app.

A typical ***state*** in our app would look like,

```jsx
// State
state = {
	loading: true,
	data: [],
	error: '',
}

// loading - Display a loading spinner in your component
// data - List of users
// error - Display error to the user
```

Here are some common ***actions,***

```jsx
// Actions
FETCH_USERS_REQUESTED - // Fetch the list of users
FETCH_USERS_SUCCEEDED - // Fetched successfully
FETCH_USERS_FAILED - // Error when fetching the data
```

These are the ***reducers,***

```jsx
// Reducers
case: FETCH_USERS_REQUESTED
			loading: true

case: FETCH_USERS_SUCCEEDED
			loading: false
			users: data // (from API)

case: FETCH_USERS_FAILED
			loading: false
			error: error // (from API)
```

## Redux Thunk Middleware

Let’s learn how to define an asynchronous action creator using `axios` & `redux-thunk` .

`axios` - requests to an API endpoint

`redux-thunk` - a middleware to define async action creators

Thunk middleware brings to the table the ability for an action creator to return a function instead of an action object.

Also, the function need not be pure. It means that the function can consist of API calls.
It has dispatch method as its arguments and thus can dispatch actions as well.

```jsx
const redux = require("redux")
const thunkMiddleware = require("redux-thunk").default
const axios = require("axios")
const createStore = redux.createStore
const applyMiddleware = redux.applyMiddleware

const initialState = {
	loading: false,
	users: [],
	error: "",
}

const FETCH_USERS_REQUESTED = "FETCH_USERS_REQUESTED"
const FETCH_USERS_SUCCEEDED = "FETCH_USERS_SUCCEEDED"
const FETCH_USERS_FAILED = "FETCH_USERS_FAILED"

const fetchUsersRequest = () => {
	return {
		type: FETCH_USERS_REQUESTED,
	}
}

const fetchUsersSuccess = users => {
	return {
		type: FETCH_USERS_SUCCEEDED,
		payload: users,
	}
}

const fetchUsersFailure = error => {
	return {
		type: FETCH_USERS_FAILED,
		payload: error,
	}
}

const reducer = (state = initialState, action) => {
	switch(action.type) {
		case FETCH_USERS_REQUESTED:
			return {
				...state,
				loading: true,
			}
		case FETCH_USERS_SUCCEEDED
			return {
				...state,
				loading: false,
				users: action.payload,
				error: "",
			}
		case FETCH_USERS_FAILED
			return {
				...state,
				loading: false,
				users: [],
				error: action.payload,
			}
		default:
			return state
	}
}

const fetchUsers = () => {
	return async function(dispatch) {
		dispatch(fetchUsersRequest())
		try {
			const { data: users } = await axios.get("https://jsonplaceholder.typicode.com/users")
			dispatch(fetchUsersSuccess(users))
		} catch (error) {
			dispatch(fetchUsersFailure(error.message))
		}
	}
}

const store = createStore(reducer, applyMiddleware(thunkMiddleware))
store.subscribe(() => console.log(store.getState()))
store.dispatch(fetchUsers())
```

Now you might ask, “All this is good. So why Redux Toolkit?”
Below is the answer to your question.

## Redux concerns

Redux requires too much boilerplate code.

- action
- action object
- action Creator
- switch statement in a reducer

A lot of other packages have to be installed to work with Redux.

- redux-thunk
- immer
- redux devtools

Therefore, Redux Toolkit!

## Redux Toolkit

Redux toolkit is the official, opinionated, batteries-included toolset for efficient Redux development.

- abstract over the setup process
- handle the most common use cases
- include some useful utilities

### Getting started with Redux Toolkit

1. Create a folder `redux-toolkit-demo` or any other name on your desktop.
2. Open the folder in your code editor, preferably Visual Studio Code.
3. Inside the folder, in your terminal, enter the command `npm init --yes`
This will initialize a `package.json` file with the default settings.
For reference,
`PS E:\GitHub\learn-redux> npm init --yes`
4. Add redux as a dependency for your project. Enter the command `npm i @reduxjs/toolkit` in your terminal.
For reference,
`PS E:\GitHub\learn-redux> npm i @reduxjs/toolkit`
5. Create an `index.js` inside your folder.

### Opinionated folder structure for Redux Toolkit

1. Create an `index.js` inside your `redux-toolkit-demo` folder.
2. Create a folder `app` inside `redux-toolkit-demo`.
3. Create a file `store.js` inside the `app` folder. This file will contain code related to our redux store.
4. Create another folder named `features` on the same level as the `app` folder. This folder will contain all features of our application.

And you’re done!

### Slice

Group together the reducer logic and actions for a single feature in a single file. And, that file name must contain `Slice` in its suffix.

The entire application state is split into slices and managed individually.

```jsx
const createSlice = require("@reduxjs/toolkit").createSlice // ES Module import

const initialState = { 
// initial state object
}

const someSliceName = createSlice({
	name: // any name,
	initialState: // the initial state,
	reducers: {
		// reducer actions
		actionName: (state, action) => {
			state.propertyName = // any value              // Direct state mutation possible
		}
	} 
})

module.exports = someSliceName.reducer                // default export
module.exports.someActionName = someSliceName.actions    // named export
```

- `createSlice` under the hood uses the *immer* library. Thus, Redux Toolkit handles the state update on our behalf.
- `createSlice` will automatically generate action creators with the same name as the reducer function (here, `actionName`) we have written.
- `createSlice` also returns the main reducer function which we can provide to our redux store.
- `createSlice` abstracts all of the boilerplate code of writing the action type constants, action object, action creators, and switch cases and also handles immutable updates.

### Configuring Store

- `configureStore` takes an object as an argument.
- The object has a key `reducer` and this reducer is where we specify all the reducers.

```jsx
const configureStore = require("@reduxjs/toolkit").configureStore; // similar to createStore in redux

const store = configureStore({
  reducer: {
    reducerOneName: // reducerOne,
  },
});
```

### Middleware

```jsx
const { getDefaultMiddleware } = require("@reduxjs/toolkit");
const reduxLogger = require("redux-logger");

const store = configureStore({
  reducer: {
    reducerOneName: // reducerOne,
    reducerTwoName: // reducerTwo,
  },
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
});
```

Example of the logger middleware terminal

```jsx
Initial State { cake: { numberOfCakes: 10 }, icecream: { numberOfIcecreams: 20 } }
 action cake/ordered @ 23:31:25.354
   prev state { cake: { numberOfCakes: 10 }, icecream: { numberOfIcecreams: 20 } }
   action     { type: 'cake/ordered', payload: undefined }
   next state { cake: { numberOfCakes: 9 }, icecream: { numberOfIcecreams: 20 } }
 action cake/ordered @ 23:31:25.357
   prev state { cake: { numberOfCakes: 9 }, icecream: { numberOfIcecreams: 20 } }
   action     { type: 'cake/ordered', payload: undefined }
   next state { cake: { numberOfCakes: 8 }, icecream: { numberOfIcecreams: 20 } }
 action cake/ordered @ 23:31:25.359
   prev state { cake: { numberOfCakes: 8 }, icecream: { numberOfIcecreams: 20 } }
   action     { type: 'cake/restocked', payload: 2 }
   next state { cake: { numberOfCakes: 10 }, icecream: { numberOfIcecreams: 20 } }
```

The `type` property has a slice name as the first part and the key of each reducer function as the second part, separated by a “**/**”.
Thus, `cake` is a slice name and there are reducer functions `ordered` & `restocked` .

### Async Actions

- Asynchronous actions in RTK are performed using `createAsyncThunk` method.
- `createAsyncThunk` method has two arguments.
- The first argument is the action name.
- The second argument is a callback function that creates the payload.
- `createAsyncThunk` automatically dispatches lifecycle actions based on the returned promise. A promise has *pending*, *fulfilled* or *rejected*. Thus, `createAsyncThunk` returns a pending, fulfilled or rejected action types.
- We can listen to these actions types by a reducer function and perform the necessary state transitions.
- The reducers though are not generated by the slice and have to be added as *extra reducers*.

```jsx
const createSlice = require("@reduxjs/toolkit").createSlice;
const createAsyncThunk = require("@reduxjs/toolkit").createAsyncThunk;
const axios = require("axios");

const initialState = {
  loading: false,
  users: [],
  error: "",
};

//Generates pending, fulfilled and rejected action types.
const fetchUsers = createAsyncThunk("user/fetchUsers", () => {
  return axios
    .get("https://jsonplaceholder.typicode.com/users")
    .then((response) => response.data.map((user) => user.id));
});

// example - a simple user slice
const userSlice = createSlice({
  name: "user",
  initialState,
  extraReducers: (builder) => {
    builder.addCase(fetchUsers.pending, (state) => {
      state.loading = true;
    });
    builder.addCase(fetchUsers.fulfilled, (state, action) => {
      state.loading = false;
      state.users = action.payload;
      state.error = "";
    });
    builder.addCase(fetchUsers.rejected, (state, action) => {
      state.loading = false;
      state.users = [];
      state.error = action.error.message;
    });
  },
});

module.exports = userSlice.reducer;
module.exports.fetchUsers = fetchUsers;
```

## React Redux setup

1. Create a react project
Now, we could also use `create-react-app` but let’s try this new frontend tooling library [vite](https://vitejs.dev/guide/).
2. Inside the root folder, in your terminal, enter the command `npm create vite@latest project-name`
This will initialize a react app named `project-name` .
3. Make the terminal point to the react project directory by entering the command `cd project-name` in the terminal.
4. Inside the folder, in your terminal, enter the command `npm install`
This will install all the required packages in `package.json` file in your app.
5. Copy & Paste the `app` and `features` folders from your `redux-toolkit-demo` folder into the `src` sub-folder of the newly created react app.
6. Install the required dependencies - `axios` , `createSlice` , `createAsyncThunk`
7. Start the server by entering the command `npm run dev`

### Provider

- Install the react-redux package in your folder. Enter the following command `npm i react-redux`
- Restart the server by entering the command `npm run dev` .
- We need to make available the store to the react app component tree. This is where the `react-redux` library comes into the picture.
- `react-redux` library exports a component called `provider` .
- Firstly import the provider component from `react-redux` library
Like this,

```jsx
// main.jsx

import { Provider } from "react-redux
import store from "./app/store"

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <Provider>
      <App />
    </Provider>
  </React.StrictMode>
)
```

- It is very important to note that the `Provider` component should be present at the top of all the components. Thus the props `store` is provided to every component in the app.
- This is because the Provider component uses `React Context` under the hood.

### useSelector

- The `useSelector` hook is used to get hold of any state that is maintained in the redux store.
- It is sort of a wrapper around `store.getState()`

```jsx
// CakeView.jsx

import React from "react"
import { useSelector } from "react-redux"

export const CakeView = () => {
  const numberOfCakes = useSelector((state) => state.cake.numberOfCakes)
  return (
    <div>
        <h2>Number of Cakes - {numberOfCakes}</h2>
        <button>Order cake</button>
        <button>Restock cakes</button>
    </div>
  )
}
```

### useDispatch

- The `useDispatch` hook is used to dispatch an action in React-Redux.
- The hook returns a reference to the dispatch function from the redux store.

```jsx
// IcecreamView.jsx

import React from "react"
import { useState } from "react"
import { useSelector, useDispatch } from "react-redux"
import { ordered, restocked } from "./icecreamSlice"

export const IcecreamView = () => {
  const [value, setValue] = useState(1)
  const numberOfIcecreams = useSelector((state) => state.icecream.numberOfIcecreams)
  const dispatch = useDispatch()

  return (
    <div>
        <h2>Number of icecream - {numberOfIcecreams} </h2>
        <button onClick={() => dispatch(ordered())}>Order cake</button>
        <input type="number" value={value} onChange={(e) => setValue(parseInt(e.target.value))}/>
        <button onClick={() => dispatch(restocked(value))}>Restock icecream</button>
    </div>
  )
}
```

```jsx
// UserView.jsx

import React, {useEffect} from "react"
import { useSelector, useDispatch } from "react-redux"
import { fetchUsers } from "./userSlice"

export const UserView = () => {
  const user = useSelector((state) => state.user)
  const dispatch = useDispatch()
  useEffect(() => {
    dispatch(fetchUsers())
  }, [])

  return (
    <div>
        <h2>List of Users</h2>
        {user.loading && <div>Loading...</div>}
        {!user.loading && user.error ? <div>Error: {user.error}</div> : null}
        {!user.loading && user.users.length ? (
          <ul>
            {user.users.map(user => (
              <li key={user.id}>{user.name}</li>
            ))}
          </ul>
        ) : null}
    </div>
  )
}
```
That's all!

There's a simple analogy I believe in & I'd like to share it with y'all.
* The **curiosity** that lead you to search for something is the first but I'd say the most important part. Remember, you are that time old when you started learning something say XYZ.
* **Consuming** the learning material (a blog, a video, or some documentation, etc.) effectively is the next important step.
* The **application** part of learning something is the one that the majority fails at.

I can't emphasize more on how important it is to apply the learnings. So, after learning Redux, I made a social-media app wherein I used Redux Toolkit. 
Live: *https://jurassic-world.netlify.app*
GitHub Repo: *https://github.com/MarkVed17/jurassic-world*

Dropping my repository link whilst I started with Redux.
*https://github.com/MarkVed17/learn-redux*

Now, if you are someone who has already stepped in the React ecosystem for a while now, you might've come across the **React Context API *versus* Redux** for state management. There's a lot of ground to cover this one. So, let's keep that topic of debate for some other day.

Until then, Keep learning! Keep growing! 😎

Let's connect on [LinkedIn](https://linkedin.com/in/vedantlahane/) & [Twitter](https://twitter.com/LahaneVedant).

Resources
- *https://youtube.com/playlist?list=PLC3y8-rFHvwiaOAuTtVXittwybYIorRB3*
- *https://redux-toolkit.js.org/*

Assets Credits
- *https://youtube.com/playlist?list=PLC3y8-rFHvwiaOAuTtVXittwybYIorRB3*