简体   繁体   中英

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

You are misreading the wrong documentation for the wrong tool it seems - in a Redux Toolkit createSlice reducer, it is always 100% correct to use something like state.push to mutably modify the object in the state variable.

What you cannot do however is what you are trying here: reassign the state variable. That had never any effect in any kind of Redux reducer, unless you would return that state variable later.

If you want to do that, you will need to return [...state, action.payload] instead and leave the state variable alone altogether - it should not be reassigned.

But the recommended way would be that push.

For more, please read Writing Reducers with Immer

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM