Search code examples
javascriptreactjsreduxreact-reduxredux-toolkit

How can i update state immutably. react, redux, redux-toolkit


I'm learning redux, and i've a method addPosts to add posts to the list of posts, and I'm doing it like this.

import { createSlice } from "@reduxjs/toolkit";

var initialState = [{ number: 1 }, { number: 2 }, { number: 3 }, { number: 4 }];

export const postsSlice = createSlice({
  name: "postsSlice",
  initialState,
  reducers: {
    addPost: (state, action) => {
      state = [...state, action.payload];
    },
  },
});

export const allPosts = (state) => state.posts;

export const { addPost } = postsSlice.actions;
export default postsSlice.reducer;

and using the state like this.

import { useSelector, useDispatch } from "react-redux";
import { addPost, allPosts } from "./postsSlice";

function Posts() {
  var posts = useSelector(allPosts);
  var dispatch = useDispatch();

  return (
    <div>
      {posts.map((post) => (
        <div>{post.number}</div>
      ))}

      {/* add post */}

      <button
        onClick={() => {
          dispatch(addPost({ number: 1 }));
          console.log(posts);
        }}
      >
        addpost
      </button>
    </div>
  );
}

export default Posts;

using state.push(action.payload) works somehow, altough the documentation says not use update state like this, and update in an immutable way. like this state = [...state, action.payload]. it does not update state with this immutable way.

I don't know what is wrong that i'm doing. thanks in advance for any help


Solution

  • As per this instead of directly changing into state you can return in this way

    return [...state, action.payload]

    Depending on your definition of initialState

    Please have a look into working example of react-redux-toolkit-slice-example

    Below is the definition of slice

    import { createSlice } from "@reduxjs/toolkit";
    
    const initialState = [{ number: 1 }];
    
    export const postsSlice = createSlice({
      name: "postsSlice",
      initialState,
      reducers: {
        addPost: (state, action) => {
          return [...state, action.payload];
        }
      }
    });
    
    export const allPosts = (state) => state.posts || [];
    
    export const { addPost } = postsSlice.actions;
    export default postsSlice.reducer;
    

    Defining the reducer(postSlice) in store

    import { configureStore } from "@reduxjs/toolkit";
    import postsReducer from "../features/posts/postsSlice";
    
    export default configureStore({
      reducer: {
        posts: postsReducer
      }
    });
    

    Use of slice in component

    import React from "react";
    import { useSelector, useDispatch } from "react-redux";
    import { addPost, allPosts } from "./postsSlice";
    
    const Posts = () => {
      var posts = useSelector(allPosts);
      var dispatch = useDispatch();
    
      return (
        <div>
          {posts.map((post, key) => (
            <div key={key}>{post.number}</div>
          ))}
    
          {/* add post */}
    
          <button
            onClick={() => {
              dispatch(
                addPost({
                  number: Math.max(...posts.map(({ number }) => number)) + 1
                })
              );
              console.log(posts);
            }}
          >
            Add Post
          </button>
        </div>
      );
    };
    
    export default Posts;