Open In App

How To Implement Optimistic Updates in Redux Applications?

Last Updated : 30 Jun, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Optimistic updates enhance user experience by making your Redux application feel more responsive. This technique involves updating the UI before receiving confirmation from the server, assuming that the update will succeed. If the server responds with an error, the application can then revert to the previous state. In this article, we will see how we can implement Optimistic Updates in Redux Application.

Prerequisites

What are Optimistic Updates?

Optimistic updates provide immediate feedback to the user by reflecting the changes in the UI as soon as an action is initiated, rather than waiting for the server response. This approach improves the perceived performance and usability of your application.

Approach

Developing a React application with Redux for state management, employing optimistic UI updates for responsiveness. Thunk handle asynchronous tasks, such as adding items, ensuring seamless user interactions. The project prioritizes modular components and thorough testing, enhancing reliability and maintainability. Using React hooks and Redux toolkit streamlines development, fostering a scalable and efficient codebase.

Step-by-Step Method

Step 1: Initialize the Project

First, create a new React application using Create React App.

npx create-react-app my-redux-app

Step 2: Navigate to project directory

cd my-redux-app

Step 3: Install Dependencies

Install the required dependencies: Redux, React-Redux, and Redux-Thunk.

npm install redux 
npm install react-redux
npm install redux-thunk

Folder Structure:

Screenshot-2024-06-18-150855
Folder Structure

The updated dependencies in package.json file will look like:

  "dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-redux": "^9.1.2",
"react-scripts": "5.0.1",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"web-vitals": "^2.1.4"
}

Update scripts in the package.json:

"scripts": {
"start": "set NODE_OPTIONS=--openssl-legacy-provider && react-scripts start",
"build": "set NODE_OPTIONS=--openssl-legacy-provider && react-scripts build",
"test": "set NODE_OPTIONS=--openssl-legacy-provider && react-scripts test",
"eject": "react-scripts eject"
}

Steps to run the application

Go to the correct directory path, then follow the steps below:

Step 1: Open command prompt, enter (For Windows)

set NODE_OPTIONS=--openssl-legacy-provider

Step 2:

npm start

Example: Illustration to implement optimistic updates in Redux applications.

JavaScript
// src/api/index.js

export const api = {
    addItem: async (item) => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if (Math.random() > 0.2) {
                    resolve({ data: item });
                } else {
                    reject(new Error('Failed to add item'));
                }
            }, 500);
        });
    },
};
JavaScript
// src/store/actions.js

import { api } from '../api';

export const addItemOptimistic = (item) => ({
    type: 'ADD_ITEM_OPTIMISTIC',
    payload: item,
});

export const addItemSuccess = (item) => ({
    type: 'ADD_ITEM_SUCCESS',
    payload: item,
});

export const addItemFailure = (error, item) => ({
    type: 'ADD_ITEM_FAILURE',
    payload: { error, item },
});

export const addItem = (item) => async (dispatch) => {
    dispatch(addItemOptimistic(item));

    try {
        await api.addItem(item);
        dispatch(addItemSuccess(item));
    } catch (error) {
        dispatch(addItemFailure(error, item));
    }
};
JavaScript
// src/store/reducer.js

const initialState = {
    items: [],
    loading: false,
    error: null,
};

export const reducer = 
(state = initialState, action) => {
    switch (action.type) {
        case 'ADD_ITEM_OPTIMISTIC':
            return {
                ...state,
                items: [...state.items, action.payload],
            };
        case 'ADD_ITEM_SUCCESS':
            return {
                ...state,
                loading: false,
            };
        case 'ADD_ITEM_FAILURE':
            return {
                ...state,
                items: state.items.filter(item => 
item.id !== action.payload.item.id),
                error: action.payload.error,
            };
        default:
            return state;
    }
};
JavaScript
// src/store/store.js

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

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

export default store;
JavaScript
//src/App.js

import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { addItemOptimistic } from './store/actions';

const App = () => {
  const [input, setInput] = useState('');
  const items = useSelector(state => state.items);
  const dispatch = useDispatch();

  const handleAddItem = () => {
    const newItem = { id: Date.now(), text: input };
    dispatch(addItemOptimistic(newItem));
    setInput('');
  };

  return (
    <div style={{ padding: '20px', 
maxWidth: '600px', margin: '0 auto' }}>
      <h1>Optimistic Updates with Redux</h1>
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
        placeholder="Add a new item"
        style={{ padding: '10px', width: '100%', 
boxSizing: 'border-box' }}
      />
      <button onClick={handleAddItem} 
style={{ marginTop: '10px', padding: '10px', width: '100%' }}>
        Add Item
      </button>
      <ul style={{ listStyle: 'none', padding: 0 }}>
        {items.map(item => (
          <li key={item.id} style={{ 
padding: '10px', borderBottom: '1px solid #ccc' }}>
            {item.text}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default App;
JavaScript
// src/index.js

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

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

Step to Run the Application: Run the application using the following command from the root directory of the project

npm start

Output: Your project will be shown in the URL http://localhost:3000/


Here's what the application might look like in action. When you add an item, it appears immediately in the list. If the server responds with an error, the item will be removed.


Next Article

Similar Reads