Day 8: Redux In-Depth

Time to get in more depth of this topic. Until now (I mean in Day 7 Blog) we have seen basics of Redux. Now we will dive in more depth as a part of this blog’s agenda.

Lets re-visit what we have learnt until now.

Redux is a state management library. It helps you manage the state of your application in a centralized way. Redux can be used with any JavaScript framework, but it's most commonly used with React.

  • State: In Redux, all the application state (data) is stored in a single object called the store.

  • Flow: Redux uses a unidirectional data flow. There are 3 main concepts:

    1. Action: Something happened (like a user clicked a button).

    2. Reducer: How should the state change in response to that action?

    3. Store: The current application state gets updated.


Lets explore (re-visit) a few key concepts.

Actions:

An action is a plain JavaScript object that describes what happened. Every action has a type property that tells Redux what kind of action occurred.

const incrementAction = {
  type: 'INCREMENT'
};

const decrementAction = {
  type: 'DECREMENT'
};

You can also pass additional data along with the action, called a payload.

const setUserAction = {
  type: 'SET_USER',
  payload: {
    name: 'John Doe',
    age: 30
  }
};

Reducers:

A reducer is a function that takes the current state and an action as input, and returns a new state. It defines how the state should change based on the action.

Example of a simple reducer:

const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
};

In this example, the counterReducer updates the state based on whether the action is 'INCREMENT' or 'DECREMENT'.

Store:

The store is the centralized place where your application state lives. You create a Redux store using the createStore function. The store manages the following:

  • The current state.

  • Dispatching actions to trigger state updates.

  • Subscribing to state changes.

To create a store:

import { createStore } from 'redux';
import counterReducer from './reducers/counterReducer';

const store = createStore(counterReducer);

Time for Live Action Practical.

This will be our end product.

Whenever user clicks on Increment Button, the Counter value increases, as per the Action. When we click on Decrement Button, based on Action specified in the Reducer code, the number value decreases.

We will start by creating a basic reducer:

const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;  // If no action matches, return current state
  }
};

export default counterReducer;

This reducer increments or decrements the state based on the dispatched action i.e (INCREMENT / DECREMENT).

Now let us create a store:

// import { createStore } from 'redux';  ----> DEPRECATED
import { legacy_createStore as createStore} from 'redux'
import counterReducer from './reducers/counterReducer';

// Create the store using the counterReducer
const store = createStore(counterReducer);

export default store;
💡
The normal createStore import seems to be deprecated. Please use the one as in above code.

The store is created using createStore and the counterReducer we just made. Next, in your main entry file (likely index.js), wrap your app inside the Provider from react-redux and pass it the store. So your index.js should look something like this.

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store'; // Import the store we created

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

Here, The Provider makes the Redux store available to all components in the app.

Now it is time to dispatch the actions in your react component. In your React component (let’s use App.js), we'll use useSelector to access the current state and useDispatch to dispatch actions.

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';  // Import hooks
import { increment, decrement } from './actions/counterActions';

function App() {
  const count = useSelector((state) => state);  // Access the state
  const dispatch = useDispatch();  // Create a dispatch function

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Counter: {count}</h1>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
}

export default App;

Here we have used 2 more hooks.

  1. useSelector Hook which retrieves the current state from the Redux store.

  2. useDispatch Hook which dispatches actions to the store. Here, we directly dispatch actions for increment and decrement.

Oh yeah, just to be clear, this is how your file structure for this mini project should look like:

After all this steps, you will be able to view your counter app in working condition. Based on Actions, the app behaves accordingly.


To keep things simple

  • Reducer defines how the state changes.

  • Store holds the state.

  • Provider makes the store available to the whole app.

  • useSelector and useDispatch hooks allow components to interact with the store.

Assignment : You can add more complexity (like actions, multiple reducers, etc.)

In conclusion, we have taken a step-by-step approach to set up a basic Redux application in React. We explored how reducers, actions, store, and hooks like useSelector and useDispatch work together to manage state. Redux might seem overwhelming initially, but with this simplified example, we have covered the key concepts of Redux and how it integrates into a React application.

Whats Next then ??

Once you're comfortable with these fundamentals, you can explore more advanced Redux features like combining multiple reducers, handling asynchronous logic with middleware, and integrating Redux DevTools for debugging.

So That is it for this blog post. Do your assignments by upgrading the example we studied here.

Until then Ciao! Happy Coding.