Search code examples
javascriptreactjsreact-nativereduxredux-toolkit

Invariant failed: A state mutation was detected between dispatches, in the path


In my app I am calling logout api(post) and AsyncStorage.clear(); then my login screen come but some how unable to store state.

[this waring get](https://i.sstatic.net/Y386X.png)

this waring in device

I am also unable to click on login screen button and other component.

This is my slice.js

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import loginService from "../../services/userService";

export const logout = createAsyncThunk("logout", async (action) => {
  const response = await loginService.logout(action);
  return response.data;
});

const userSlice = createSlice({
  name: 'userDetails',
  initialState: {
    loading: false,
    error: null,
    message: null,
    success: false,
    details: null,
    secret: null,
    authorizeSecret: null,
    isPwdReseted: false,
    deviceInfomation: {},
  },
  extraReducers: (builder) => {
    builder.addCase(logout.pending, (state, action) => {
      state.loading = true;
      state.success = false;
    });
    builder.addCase(logout.fulfilled, async (state, action) => {
      state.loading = false;
      state.success = action.payload.Success;
      state.message = action.payload.Message;
      if (action.payload.Success && action.payload.StatusCode === 200) {
        await AsyncStorage.clear();
        console.log('Storage Clear');
      }
    });
    builder.addCase(logout.rejected, (state, action) => {
      state.loading = false;
      state.success = false;
      state.error = action.payload
    });
  }
});

this is console log

Possible Unhandled Promise Rejection (id: 2):
Error: Invariant failed: A state mutation was detected between dispatches, in the path 'userDetails.loading'.  This may cause incorrect behavior. (https://redux.js.org/style-guide/style-guide#do-not-mutate-state)
Error: Invariant failed: A state mutation was detected between dispatches, in the path 'userDetails.loading'.  This may cause incorrect behavior. (https://redux.js.org/style-guide/style-guide#do-not-mutate-state)

I know some how I didn't get initial state for my login screen, but why I don't know.


Solution

  • Issue

    The logout.fulfilled reducer case function is declared async and so implicitly returns a Promise object instead of the next state.

    builder.addCase(
      logout.fulfilled,
      async (state, action) => { // <-- implicit Promise returned
        state.loading = false;
        state.success = action.payload.Success;
        state.message = action.payload.Message;
        if (action.payload.Success && action.payload.StatusCode === 200) {
          await AsyncStorage.clear();
          console.log('Storage Clear');
        }
      }
    );
    

    Redux reducer functions are to be pure, synchronous functions.

    Solution

    Move the asynchronous "side-effect" of clearing the storage into the asynchronous action. Update the logout.fulfilled reducer function to be synchronous and only update the state.

    import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
    import loginService from '../../services/userService';
    
    export const logout = createAsyncThunk(
      "logout",
      async (action) => {
        const response = await loginService.logout(action);
    
        if (response.Success && response.StatusCode === 200) {
          await AsyncStorage.clear();
          console.log('Storage Clear');
        }
    
        return response.data;
      }
    );
    
    const userSlice = createSlice({
      name: "userDetails",
      initialState: {
        ...
      },
      extraReducers: (builder) => {
        builder
          .addCase(logout.pending, (state, action) => {
            state.loading = true;
            state.success = false;
          });
          .addCase(logout.fulfilled, (state, action) => {
            state.loading = false;
            state.success = action.payload.Success;
            state.message = action.payload.Message;
          });
          .addCase(logout.rejected, (state, action) => {
            state.loading = false;
            state.success = false;
            state.error = action.payload
          });
      },
    });