DevLoverUmar
DevLoverUmar

Reputation: 14001

How can I get the state value in the reducer of createSlice?

I'm using redux-toolkit in my react project. In a reducer of createSlice, I want to use the existing array of entities from the state and append the new array,before reducing the final state. But I'm unable to get the state value.

Here is the reducer code

export const usersSlice = createSlice({
  name: "users",
  initialState: initialUsersState,
  reducers: {
    usersCreated: (state: UsersState, action) => {
      // in real, return count from the server and append the entities on front-end only?
      const { count, entities } = action.payload;
      const existingEntities = state.entities;
      const newEntities = [...existingEntities, ...entities];
      const totalCount = state.totalCount+count;
      return {
        ...state,
        entities: newEntities,
        totalCount: totalCount,
        listLoading: false,
        error: null,
      };
    },
}});

When I debug the state.entites variable, it looks like this

enter image description here

Is there a way to access the current state value in reducer/extraReducer to recreate the state as per desire?

Because I assume directly working with state value outside the reducer would be a bad practice. Please guide me, if I'm wrong.

Edit

The code sandbox created by @Linda Paiste is working fine, that means we can access the state variable in reducer but we can't debug the state variable to dig deeper what is that state variable is holding at the moment, as Redux-toolkit is dealing the state in it's own way... As obvious from the debugging screenshot

enter image description here

Upvotes: 27

Views: 36770

Answers (5)

vikrant gupta
vikrant gupta

Reputation: 103

You can use current function provided by toolkit out of the box to get the current state value.

import {
    createSlice,
    current
} from '@reduxjs/toolkit';

export const parserSlice = createSlice({
    name: ReducerKeys.parser,
    initialState,
    reducers: {
        updateData: (state, action) => {
            console.log(action.payload, current(state), 'updating');
        }
    }
});

Upvotes: 10

Mohamed A. Bahrawy
Mohamed A. Bahrawy

Reputation: 21

You can use this function to retrieve a copy of the real state:

const getCurrentState = (state) => {
  try {
    return JSON.parse(JSON.stringify(state));
  } catch (e) {
    return null;
  }
};

then use this inside the reducer:

const currentState = getCurrentState(state);

Upvotes: 1

The Happy Monkey
The Happy Monkey

Reputation: 121

I am simply copying/pasting an equivalent of the CodeSandbox solution by @Linda Paiste, so it's faster to find and reuse.

const usersSlice = createSlice({
  name: "users",
  initialState: {
   users: [],
  },
  reducers: {
    userCreated: (state, action) => {
      const newUser = action.payload;
      const existingUsers = JSON.parse(JSON.stringify(state.users));
      const newUsers = [...existingUsers, newUser];
      state.users= newUsers;
    }
  }
});

Upvotes: 4

Linda Paiste
Linda Paiste

Reputation: 42228

Summarizing the info in the comments from myself and @karlmaxlopez:

I copied your code into a CodeSandbox demo and found that the code does execute as expected. You are able to append to the state.entities array as if it were a normal array, even though the value of state.entities appears as a Proxy when inspecting or null when logging.

This is because redux-toolkit uses Immer to prevent you from directly mutating the state. In a normal redux reducer you receive the previous state as a function argument. The responsibility is placed on you as the user to not mutate it and instead to return a new object with the updated values by using non-mutating methods such as object spreading, array concat, etc. This is what you have done in your usersCreated reducer, which is totally fine.

But redux-toolkit and Immer opens up an additional possibility for how to write a reducer. In an Immer reducer, the state argument that is received by your function is a "draft" object. This means that you can directly mutate the draft object without causing any issues because it is just a draft and not the true state.

It also means that when you try to examine this previous state, it will behave strangely because of being just a draft and not the actual object that you are expecting it to be.

If you want to console.log a draft value, you can do this by calling console.log(current(value)) using the immer current function, which is included in the redux toolkit package. (edited per @markerikson's comment).

As far as inspecting the changes made to your state by each action, I recommend using Redux DevTools. I am able to see what actions were dispatched, the current state, what changes were made, etc. Redux DevTools screenshot

Upvotes: 23

Dennis Vash
Dennis Vash

Reputation: 53934

You can only reference the current slice state.

Therefore your only options are passing the desired entities as action's payload or implementing this action as a thunk createAsyncThunk and use getState() from its API.

Upvotes: 2

Related Questions