Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use a state property inside the prepare callback (Redux Toolkit)

I have a store with this initialState:

const initialState = {
 todos : [] // [{ task: "todo something", description: "have todo something" },  ...] 
 maxTodos: 5
 ....
}

I want inside the prepare callback to id new todo that I will add, based on the length of the current todos array.

reducers: {
  addTodo: {
    reducer: (state, action) => {
      return { ...state, todos: [...state.todos, action.payload]}
    },
    prepare: (state, text) => {
      const id = state.todos?.length; // the error is some what here
      return { payload: { id, text } }
    },
  },
....

Is there a way to access state inside the prepare callback to the slice state?

Or a different approach that can solve this?


The easy solution to send from the dispatch the length of the todo's or the create a nanoId() inside the prepare.

For now I don't want to implement this approach (written above), I want to send via the dispatch only the new todo that I want to add, and the id will be transparent to the dispatch because I want to control the todo's via their position in the array.

like image 767
Yaron Avatar asked Feb 03 '26 11:02

Yaron


1 Answers

The prepare function is used to preprocess the action object prior to the reducer function receiving it. It's a way to write action creators that take more than a single argument, or to add meta data to the action object.

I don't see much of a use case here for adding an id property that is the length of the array in state from the prepare function. The prepare function just doesn't have access to the state object. My suggestion would be to just compute the id in the reducer function where the state is accessible.

Example:

reducers: {
  addTodo: (state, action) => {
    state.todos.push({
      text: action.payload,
      id: state.todos.length,
    })
  },
...

This would possibly be a bit silly, but if you really just wanted an action creator that returned a payload with the text and a generated id property based on the current state you could use createAsyncThunk and access getState from the ThunkAPI.

Example:

const addTodo = createAsyncThunk(
  "todos/addTodo",
  (text, { getState }) => ({
    text,
    id: getState().todos.length, // *
  })
);

const todosSlice = createSlice({
  name: "todos",
  initialState,
  reducers: {
    ...
  },
  extraReducers: builder => {
    builder.addCase(addTodo.fulfilled, (state, action) => {
      state.todos.push(action.payload);
    });
  },
});

*Note: Edit to select correct state based on how the reducers are combined to form the root reducer passed to the store configurator.

like image 173
Drew Reese Avatar answered Feb 05 '26 03:02

Drew Reese