Open In App

How to Handle Errors in React Redux applications?

Last Updated : 14 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

To handle errors in Redux applications, use try-catch blocks in your asynchronous action creators to catch errors from API calls or other async operations. Dispatch actions to update the Redux state with error information, which can then be displayed to the user in the UI using components like error modals or notifications. Utilize middleware like Redux Thunk or Redux Saga for more complex error-handling logic.

Syntax of try-catch Block:

export const fetchSomething = () => async (dispatch) => {
try {
const response = await axios.get('/api/something');
dispatch({ type: 'FETCH_SOMETHING_SUCCESS', payload: response.data });
} catch (error) {
dispatch({ type: 'FETCH_SOMETHING_FAILURE', payload: error.message });
}
};

Approach to handle errors in Redux Applications:

To handle errors in Redux applications like the provided code, you can follow these steps:

  • Use async actions with Redux Thunk or Redux Toolkit’s createAsyncThunk for API requests.
  • Handle errors within async action creators using try-catch blocks to catch and manage any exceptions.
  • Update the Redux state with error information in the appropriate action type (e.g., fetchTodos.rejected).
  • In your React components, conditionally render error messages or UI elements based on the Redux state’s error field to inform users about any errors that occur during data fetching.

Steps to Create a React App and Installing Modules

Step 1: Create a new react app and enter into it by running the following commands

npx create-react-app redux-error-handling

Step 2: After creating your project folder i.e. foldername, move to it using the following command.

cd redux-error-handling

Project Structure:

ps-2

project structure

Updated Dependencies: The updated dependencies in package.json file should look like this.

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

Example: Implementation to showcase handling of error in redux application.

CSS
/* src/App.css */
 
.container {
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
}

.top-text {
  font-size: 20px;
  font-weight: 500;
  color: rgb(28, 158, 37);
  
}

.t-2-text {
  font-size: 12px;
  text-decoration: underline;
}

.fetch-button {
  padding: 10px 20px;
  margin-bottom: 20px;
  font-size: 16px;
  background-color: #f2176b;
  color: #fff;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.fetch-button:hover {
  background-color: #bf2419;
}

.loading {
  margin-top: 20px;
  font-size: 60px;
}

.todos-list {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 12px;
  font-size: 12px;
}

.cards {
  padding: 20px;
  border-radius: 5px;
}

.top {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 10px;
}

.loader {
  width: 50px;
  --b: 8px; 
  aspect-ratio: 1;
  border-radius: 50%;
  padding: 1px;
  background: conic-gradient(#0000 10%,#f03355) content-box;
  -webkit-mask:
    repeating-conic-gradient(#0000 0deg,#000 1deg 20deg,#0000 21deg 36deg),
    radial-gradient(farthest-side,#0000 calc(100% - var(--b) - 1px),#000 calc(100% - var(--b)));
  -webkit-mask-composite: destination-in;
          mask-composite: intersect;
  animation:l4 1s infinite steps(10);
}
@keyframes l4 {to{transform: rotate(1turn)}}
Javascript
// src/redux/store.js
 
import { configureStore } from '@reduxjs/toolkit';
import todosReducer from './userSlice';
 
const store = configureStore({
    reducer: {
        todos: todosReducer,
    },
});
 
export default store;
Javascript
// src/redux/userSlice.js
 
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
 
export const fetchTodos = createAsyncThunk(
    'todos/fetchTodos',
    async () => {
        try {
            const response = await 
                fetch('https://jsonplaceholder.typicode.com/todos');
            if (!response.ok) {
                throw new Error('Failed to fetch todos');
            }
            const todos = await response.json();
            return todos;
        } catch (error) {
            throw new Error('Error fetching todos');
        }
    }
);
 
const todosSlice = createSlice({
    name: 'todos',
    initialState: {
        data: [],
        loading: false,
        error: null,
    },
    reducers: {},
    extraReducers: (builder) => {
        builder
            .addCase(fetchTodos.pending, (state) => {
                state.loading = true;
                state.error = null;
            })
            .addCase(fetchTodos.fulfilled, (state, action) => {
                state.loading = false;
                state.data = action.payload;
            })
            .addCase(fetchTodos.rejected, (state, action) => {
                state.loading = false;
                state.error = action.error.message;
            });
    },
});
 
export default todosSlice.reducer;
Javascript
// src/App.js

import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchTodos } from './redux/userSlice';
import './App.css';
import Loader from './components/Loader';

function App() {
  const dispatch = useDispatch();
  const todos = useSelector((state) => state.todos);

  const randomLightColor = () => {
    const randomColor = () => Math.floor(Math.random() * 200) + 56;
    return `rgb(${randomColor()}, ${randomColor()}, ${randomColor()})`;
  };

  return (
    <div className="container">
      <div className='top'>
        <h1 className='top-text'>GFG Todo App</h1>
        <h1 className='t-2-text'>
            Any errors are being handled using Thunk in Redux
        </h1>
        <button className="fetch-button" onClick={
          () => dispatch(fetchTodos())
        }>
          Fetch Todos</button>
      </div>
      <div className='top'>
        {todos.loading && <p className="loading"><Loader /></p>}
        <div className="todos-list">
          {todos.data?.map((todo) => (
            <div className='cards' 
                 style={{ backgroundColor: randomLightColor() }} 
                 key={todo.id}>{todo.title}</div>
          ))}
        </div>
      </div>
    </div>
  );
}

export default App;
Javascript
// src/index.js
 
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(
    <Provider store={store}>
        <App />
    </Provider>
);
Javascript
// src/components/Loader.js

import React from 'react'

const Loader = () => {
  return (
    <div className='loader'></div>
  )
}

export default Loader

Output:

2024-03-1104-04-53-ezgifcom-video-to-gif-converter

output




Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads