What Is The Redux In React?

In a React app, state refers to the data that changes over time, such as a counter, user information, or shopping cart items. Normally, we store state inside components using userState or this.state, but in large apps, many components require the same state, and passing it via props becomes cumbersome.

Redux solves this problem with the following methods:

  • Centralized State: This means all the important data of your app is stored in one central place called the store.
  • Predictable Updates: Components cannot change state directly. They dispatch actions, which describe what happened, and a reducer decides how the state should change. A reducer is a special function that determines how the state changes when an action is sent. It’s called pure because. It gives the same output for the same input.
  • Consistency Across Components: Any component can access the same state from the store without relying on props drilling.

Real-life Analogy:

Imagine of redux like a library of books:

  • The store is the library where all books (data) are kept.
  • Actions are requests like “Add a book” or “Update a book”.
  • Reducers are the libraries that decide how to update the library based on the request.
  • Any student (component) can read the books without needing the books in their own backpack.

How To Setup React Redux?

When you want to use Redux in a React project, you need two things:

  • Redux → This is the main library that provides the core tools to create a store, reducers, and actions.
  • React-Redux → This is a helper library that connects your React components to the Redux store, allowing them to read data and dispatch actions easily.

These two packages are important because Redux alone can work in any JavaScript app, not just React. React-Redux is a bridge that makes Redux work smoothly with React components.

How To Install Redux?

The following command will download both libraries into your project, allowing you to use them.

npm install redux react-redux

After successfully installation, you can:

  • Create a store to hold your app state.
  • Write reducers to decides how the state changes.
  • Use provider from react-redux to make the store available to all components.
  • Use hooks like useSelector to read state and useDispatch to send actions.

Simple Example After Installation

1) Create Store.js File:

// store.js
import { createStore } from 'redux';

const initialState = { message: "Hello Redux" };

function messageReducer(state = initialState, action) {
return state; // For now, it just returns the same state
}

const store = createStore(messageReducer);
export default store;

2) Create App.js File:

// App.js
import React from 'react';
import { Provider, useSelector } from 'react-redux';
import store from './store';

function Message() {
const msg = useSelector(state => state.message); // Get state
return <h1>{msg}</h1>;
}

export default function App() {
return (
<Provider store={store}>
<Message />
</Provider>
);
}

Please review this code carefully. Installed libraries allow us to create the store and connect components with React-Redux.

Core Redux Components

1) Store – The Central Brain

  • Think of the store as the single place where all your data is kept.
  • No matter how many components you have, the data lives in one central container.
  • This makes your app consistent because every component looks at the same source.

Simple Example:

import { createStore } from 'redux';

const initialState = { count: 0 };

function counterReducer(state = initialState, action) {
return state;
}

const store = createStore(counterReducer);
console.log(store.getState()); // { count: 0 }

2) Actions – Your To-Do List

  • An action is like a note saying “What do you want to do with the data?”
  • It doesn’t do the change itself; it just describes what needs to happen.
  • Every action must have a type and can have extra info.

Example of Actions:

const increaseAction = { type: 'INCREASE' };
const setAction = { type: 'SET_COUNT', payload: 5 };

In this code:

  • First one says: “Please increase the count.”
  • Second one says: “Set the count to 5.”

3) Reducers – The Decision Maker

  • A reducer is a function that reads the current state and the action, and then determines the new state.
  • It does not change the old state directly. Instead, it creates a new state object.

Simple Example of Reducer:

function counterReducer(state = { count: 0 }, action) {
switch (action.type) {
case 'INCREASE':
return { count: state.count + 1 };
case 'SET_COUNT':
return { count: action.payload };
default:
return state;
}
}

4) Dispatch – The Messenger

  • In Redux, you cannot directly change the state. Dispatch means how you send an action to the store.
  • When you dispatch an action, the store gives it to the reducer, which updates the state.

For example:

store.dispatch({ type: 'INCREASE' });
console.log(store.getState()); // { count: 1 }

store.dispatch({ type: 'SET_COUNT', payload: 10 });
console.log(store.getState()); // { count: 10 }

Creating a Basic Redux Store

Now, we creating a simple counter store

import { createStore } from 'redux';

// Step 1: Define the initial state
const initialState = {
clicks: 0
};

// Step 2: Create a reducer function
function clickReducer(state = initialState, action) {
switch (action.type) {
case 'ADD_CLICK':
return { ...state, clicks: state.clicks + 1 };
case 'RESET_CLICK':
return { ...state, clicks: 0 };
default:
return state; // If no matching action, return current state
}
}

// Step 3: Create the store using the reducer
const store = createStore(clickReducer);

// Step 4: Check the store in action
console.log('Initial State:', store.getState());

store.dispatch({ type: 'ADD_CLICK' });
console.log('After 1 Click:', store.getState());

store.dispatch({ type: 'RESET_CLICK' });
console.log('After Reset:', store.getState());

export default store;

In this example:

  • initialState: We start with { clicks: 0 } because no clicks have occurred yet.
  • clickReducer: This is the decision maker.

How To Connect Redux with React?

When you create a Redux store, it lives outside React by default. But your components need access to that store to:

  • Read data (state)
  • Update data (dispatch actions)
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import App from './App';
import store from './store'; // Our Redux store

// Connecting Redux to React
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);

Explanation:

  • <Provider store={store}>: This component makes sure every React component inside <App /> can talk to Redux. Without this, your React components wouldn’t know about the store.

Using React-Redux Hooks

Now we learn how to use React-Redux Hooks. It provides hooks such as useSelector and useDispatch to access the state and dispatch actions from within React components.

  1. useSelector
  2. useDispatch

Example: Create a Simple Voting System where students vote for React or Redux:

Reducer (store.js):

import { createStore } from 'redux';

const initialState = { reactVotes: 0, reduxVotes: 0 };

function voteReducer(state = initialState, action) {
switch (action.type) {
case 'VOTE_REACT':
return { ...state, reactVotes: state.reactVotes + 1 };
case 'VOTE_REDUX':
return { ...state, reduxVotes: state.reduxVotes + 1 };
default:
return state;
}
}

const store = createStore(voteReducer);
export default store;

React Component (VoteApp.js):

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';

function VoteApp() {
const reactVotes = useSelector((state) => state.reactVotes);
const reduxVotes = useSelector((state) => state.reduxVotes);
const dispatch = useDispatch();

return (
<div style={{ textAlign: 'center', marginTop: '50px' }}>
<h1>Vote for Your Favorite!</h1>
<p>React: {reactVotes} | Redux: {reduxVotes}</p>
<button onClick={() => dispatch({ type: 'VOTE_REACT' })}>Vote React</button>
<button onClick={() => dispatch({ type: 'VOTE_REDUX' })}>Vote Redux</button>
</div>
);
}

export default VoteApp;

index.js (Connecting Redux to React):

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import VoteApp from './VoteApp';

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

In this code, the Redux store has two values: reactVotes and reduxVotes. The useSelector reads the latest vote counts.

Then, useDispatch sends an action like { type: ‘VOTE_REACT’ } when you click a button. Now, Reducer updates the count, and the UI re-renders automatically.

Middleware in Redux

Normally, in Redux, you send an action, and the reducer updates the state; then, the UI re-renders. This means the actions are plain objects, like { type: “ADD_ITEM” }.

But what if you need to wait for something before updating the state, like: Fetching data from an API, Waiting for a timer, or Writing logs before updating?

For these problems, we used a middleware. It acts as a helper between the action and the reducer. It can pause the process, perform additional tasks (such as logging and fetching data), and then continue by sending an action to the reducers.

What Is Redux-Thunk Middleware?

Thunk middleware allows you to dispatch functions instead of objects, so you can do async operations (like API calls) before updating the store.

Install redux-thunk:

npm install redux-thunk

Configure Store with Middleware:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(rootReducer, applyMiddleware(thunk));

export default store;

Example of an Async Action with Thunk:

const fetchData = () => {
return async (dispatch) => {
const response = await fetch('/api/data');
const data = await response.json();
dispatch({ type: 'SET_DATA', payload: data });
};
};

Explanation:

  • fetchData work as an asynchronous action creator that fetches data from an API and then dispatches an action with the fetched data as payload.

Redux DevTools

Redux DevTools is a browser extension that provides a time-travel debugging experience, allowing developers to inspect and debug state changes in the store.

Installing Redux DevTools:

npm install redux-devtools-extension

Enabling Redux DevTools in Store Configuration:

import { createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';

const store = createStore(rootReducer, composeWithDevTools());

Also Learn Other Topics:

Simple Student Exercise

“Student Task Tracker with Theme & API Integration”

Create a Student Task Tracker App using React + Redux. The app should allow:

  1. Add Tasks → Example: “Complete Redux Assignment”.
  2. Toggle Task Completion → Mark task as done or not done.
  3. Delete Task → Remove a specific task.
  4. Theme Switch → Light / Dark mode toggle.
  5. Fetch Extra Tasks from API → Using Redux-Thunk middleware.
  6. Enable Redux DevTools for debugging.

Requirements of this simple project

Use Redux for state management.
State should have two parts:

  • tasks → An array of task objects { id, title, completed }
  • theme → “light” or “dark”.

Actions:

  • ADD_TASK → Add a new task.
  • TOGGLE_TASK → Toggle completion.
  • DELETE_TASK → Remove a task.
  • TOGGLE_THEME → Switch theme.
  • SET_API_TASKS → Add tasks from API.
    Middleware: Use Redux Thunk for async API calls.
    Hooks: Use useSelector and useDispatch.
    Provider: Wrap the app with Provider and connect the store.
    Enable Redux DevTools using composeWithDevTools().

You can write this code yourself without any other help.

Leave a Comment