javascriptreactjsredux-toolkit

Problem in Redux Toolkit's Async Thunks: State Not Properly Updated in React Component After Actions Are Dispatched


I am working on a React application where I use Redux Toolkit for state management. I have a complex setup where I'm dispatching an async thunk to fetch data from a Node.js backend. Data fetching is performed correctly, but there's a problem whereby it doesn't update properly in the state of the component after dispatch.

I'm sending an async thunk for fetching user details inside my React component. The data gets fetched correctly, but somehow, the component doesn't seem to re-render with a newly updated state. I can confirm that in Redux, the state does get updated, but somehow it doesn't reach my component.

Question

Why is my React component not re-rendering when the state changes in Redux, although the state in Redux DevTools does show the correct data? Possibly what I am missing in this setup? Any help or guidelines on how to debug much appreciated!

Redux Slice:

// userSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';

export const fetchUserDetails = createAsyncThunk(
  'user/fetchUserDetails',
   async (userId) => {
     const response = await axios.get(`/api/users/${userId}`);
     return response.data;
   }
);

const userSlice = createSlice({
   name: 'user',
   initialState: {
   userDetails: {},
   status: 'idle',
   error: null,
},
reducers: {},
    extraReducers: (builder) => {
        builder
        .addCase(fetchUserDetails.pending, (state) => {
            state.status = 'loading';
        })
        .addCase(fetchUserDetails.fulfilled, (state, action) => {
            state.status = 'succeeded';
            state.userDetails = action.payload;
        })
        .addCase(fetchUserDetails.rejected, (state, action) => {
            state.status = 'failed';
            state.error = action.error.message;
        });
    },
});

export default userSlice.reducer;

React Component:

// UserProfile.jsx
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUserDetails } from '../slices/userSlice';

const UserProfile = ({ userId }) => {
  const dispatch = useDispatch();
  const { userDetails, status, error } = useSelector((state) => state.user);

  useEffect(() => {
    if (status === 'idle') {
      dispatch(fetchUserDetails(userId));
    }
  }, [dispatch, userId, status]);

  if (status === 'loading') {
      return <div>Loading...</div>;
  }

  if (status === 'failed') {
    return <div>Error: {error}</div>;
  }

  return (
    <div>
      <h1>{userDetails.name}</h1>
      <p>Email: {userDetails.email}</p>
    </div>
  );
};

export default UserProfile;

Solution

  • The problem you're facing, in which the React component won't re-render despite the Redux state updating, is almost always related to how React determines whether or not it should re-render a component. Here are some reasons and possible solutions for that: