Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

State will not Update when using createAsyncThunk and Redux Toolkit

When using createAsyncThunk to obtain data from my API, I can read the data, but the state will not update.

I created a slice in my file userSlice.js, like so

userSlice.js:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

export const fetchUserById = createAsyncThunk(
  'user/fetchUserById',
  async (userId, thunkAPI) => {
    let response;
    try {
      response = await API.getUser, { userID: userId }));
    } catch (e) {
        console.log(e)
    }
    return response.data.getUser
  }
);

export const userSlice = createSlice({
  name: 'user',
  initialState: {user: {}},
  reducers: {},
  },
  extraReducers: (builder) => {
    builder.addCase(fetchUserById.fulfilled, (state, action) => {
      state = {
        ...state,
        user: action.payload
      }
    });
  }
});

export default userSlice.reducer

This is my store, store.js:

import { configureStore } from '@reduxjs/toolkit'
import userReducer from './slices/userSlice'

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

I try to read the data in AppLoadingScreen.js

AppLoadingScreen.js:

import AppLoading from 'expo-app-loading';
import React from 'react';
import { useSelector, useDispatch } from 'react-redux'
import { fetchUserById } from '../../redux/slices/userSlice';

const AppLoadingScreen = (props) => {
  const user = useSelector(state => state.userReducer.user)
  const dispatch = useDispatch()
  
  const cacheResources = async () => {
    dispatch(fetchUserById('00'))
      .unwrap()
      .then((result) => console.log("result: ", result))
      .then(console.log("in dispatch: ", user))
      .catch((e) => {console.log(e)});

    console.log("test: ", user);
  }

  return (
    <AppLoading
      startAsync={cacheResources}
      onFinish={() => {props.setIsReady(true)}}
      onError={console.warn}
    />
  );
}

Output:

in dispatch:  Object {}
test:  Object {}
result:  Object {
  "firstName": "John",
  "lastName": "Doe",
  "height": 72,
  "age": 40,
}

In both cases, user does not update. Even if I change the dispatch call to instead use await, the state doesn’t change.
New dispatch call:

const cacheResources = async () => {
  await dispatch(fetchUserById('00'));
  
  console.log("test: ", user);
}

New output:

test:  Object {}

I have also tried running the dispatch call outside of Expo’s AppLoading component and the result is still the same.

>Solution :

state = in an RTK Immer-powered reducer is never correct. Immer works by tracking mutations to nested fields (state.someField =), or returning an entirely new value from the reducer function ( return newState). When you write state =, all that does is point the local variable named state at a new reference, which is neither a mutation nor returning anything.

So, as far as Immer can see, you haven’t changed anything.

The simplest answer here is to write state.user = action.payload.

See RTK Usage Guides: Writing Reducers with Immer for more details.

Additionally, we specifically recommend not using the word "reducer" in your state structure. Instead, you should set up the store so that the state keys are named after the types of data, like:

configureStore({
  reducer: {
    user: userReducer
  }
})
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading