Satyam Arya

Introduction to Redux: Managing Application State in JavaScript

Redux is a state management library for JavaScript applications, commonly used in conjunction with React. It provides a predictable state container, which allows developers to manage the application state in a more organized and efficient way.

As we know, React follows the component-based approach, where the data flows through the components. In fact, the data in React always flows from parent to child components which makes it unidirectional

But what happens when we try to communicate from a non-parent component?

A child component can never pass data back up to the parent component. React does not provide any way for direct component-to-component communication. Even though React has features to support this approach, it is considered to be a poor practice. It is prone to errors and leads to spaghetti code.

This is where React fails to provide a solution and Redux comes into the picture

redux

Redux provides a “store” as a solution to this problem. A store is a place where you can store all your application state together. Now the components can “dispatch” state changes to the store and not directly to the other components. Then the components that need the updates about the state changes can “subscribe” to the store. This is how Redux makes the data flow easier.

Redux Cycle

The Redux cycle is the series of steps that occur when an action is dispatched and the state of the application is updated.

Getting Started with Redux

Install the following packages:

npm install @reduxjs/toolkit react-redux

Create Reducers & Actions using createSlice

import { createSlice } from "@reduxjs/toolkit";

const messageSlice= createSlice({
    name: 'messageSlice',
    initialState: {
        message: ''
    },
    reducers: {
        setMessage: (state, action) => {
            state.message = action.payload;
        },
        resetMessage: (state, action) => {
            state.message = '';
        }
    }
})

export default messageSlice.reducer;
export const { setMessage, resetMessage } = messageSlice.actions;

Add Slice Reducers to the Store

A store is a JavaScript object which can hold the application’s state and provide a few helper methods to access the state, dispatch actions and register listeners. The entire state of an application is saved in a single store

import { configureStore } from "@reduxjs/toolkit";
import messageReducer from "./slices/messageSlice";

export default configureStore({
    reducer: {
        messageReducer
    }
})

Add store provider to the App

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import store from "./redux/store";

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

Dispatch actions in Components

Now we can use the React-Redux hooks to let React components interact with the Redux

import React, {useRef} from 'react'
import { useDispatch } from 'react-redux';
import { setMessage } from '../redux/slices/messageSlice';

const Child1 = () => {

    const inputRef = useRef(null);
    const dispatch = useDispatch();

    function handleSubmit(e){
        e.preventDefault();
        const inputTextValue = inputRef.current.value;
        dispatch(setMessage(inputTextValue));
    }

  return (
    <div>
        <form>
            <input type="text" ref={inputRef}/>
            <input type="submit" onClick={handleSubmit} />
        </form>
    </div>
  )
}

export default Child1;

Read state data in components

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

const Child2 = () => {
    const message = useSelector(state => state.messageReducer.message);

  return (
    <div>The data from child is: {message}</div>
  )
}

export default Child2;

Redux DevTools Extension: Click here

Redux Thunk

Redux Thunk is a middleware for Redux. It allows you to write asynchronous logic that interacts with the Redux store. By default, Redux actions are synchronous, which means that they happen immediately when they are dispatched. However, sometimes you need to perform asynchronous tasks, like fetching data from an API, before updating the store. This is where Redux Thunk comes in.

To see visual representation: Click here

Example:

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";

export const fetchData = createAsyncThunk("products/fetch", async () => {
    const response = await fetch('https://api.escuelajs.co/api/v1/products');
    return await response.json();
  })

const productSlice = createSlice ({
    name: 'productSlice',
    initialState: {
        products: [],
        status: "idle",
        error: null,
    },
    extraReducers: function (builder) {
        builder
            .addCase(fetchData.pending, (state, action) => {
                state.status = "loading";
            })
            .addCase(fetchData.fulfilled, (state, action) => {
                state.products = action.payload;
                state.status = "success";
            })
            .addCase(fetchData.rejected, (state, action) => {
                state.status = "failed";
                state.error = action.error.message;
            })
    }
})

export default productSlice.reducer;
export const {loadProducts} = productSlice.actions;

Now, dispatch the async Thunk from Components

import React, {useEffect} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchData } from '../../redux/slices/productSlice';
import SingleProduct from '../singleProduct/SingleProduct';

const ProductList = () => {

  const dispatch = useDispatch();
  const products = useSelector(state => state.productReducer.products)
  const status = useSelector(state => state.productReducer.status)

  useEffect(() => {
    dispatch(fetchData())         //dispatch to the middleware
  }, [])

  if(status === 'loading'){
    return <h2 className='loading'>Loading...</h2>
  }
  if(status === 'failed'){
    return <h2 className='loading'>Uh oh! Something went wrong</h2>
  }

  return (
    <div className="productList">
        {products.map((item) => (<SingleProduct key={item.id} product={item} />))}
    </div>
  )
}

export default ProductList;

In conclusion, Redux is a JavaScript state management library that provides a predictable way to manage the state of an application. Redux works by providing a centralized store that stores the entire state of the application, and a series of reducers that process actions to update the state of the application. By using a predictable cycle of dispatching actions, updating the state, and notifying the UI, Redux provides a clear separation between the state and the UI, making it easier to reason about the state of the application and to maintain a consistent state throughout the application.

Check our other articles: https://satyam-arya.click/understanding-react-hooks/

Exit mobile version