react数据集中式管理第二篇--@reduxjs/toolkit

801 阅读6分钟

上一篇介绍了redux的基本知识点,今天一起来学习下@reduxjs/toolkit。

其他几篇:

react数据集中式管理第一篇--Redux初认识

react数据集中式管理第三篇--redux-saga

react数据集中式管理第四篇--@rematch/core

什么是@reduxjs/toolkit

@reduxjs/toolkit 就是redux的一个工具包,它想编写Redux逻辑的标准方式,想解决配置Redux存储太复杂的问题;

先放上我的项目部分目录结构:

image.png \


下面一起学习@reduxjs/toolkit中的createAction、counterReducer、createSlice

主角之一:createAction:

之前我们要写一个action时,要自己写 const action = {type: xxx,  payload:  xxx}; 如果action一多起来每次都要写什么type,payload就会很繁琐;这时候createAction就发挥它的作用;

createAction:返回给定action type 字符串的 action creator 函数,该函数本身已定义了 toString() ,因此可以代替常量类型使用;

注意:createAction返回的是一个函数,那么这个函数的参数就是payload的值,上代码:console.log("getAgentUserAction", getAgentUserAction({})); // {type: "posts/update", payload: {}}

// store/modules/action.js 存放所有的createAction 
import { createAction } from "@reduxjs/toolkit";

export const getAgentUserAction = createAction("posts/update"); 
// 返回给定 action type 字符串的 action creator 函数,该函数本身已定义了 toString(),因此可以代替常量类型使用 
console.log("getAgentUserAction", getAgentUserAction({})); // {type: "posts/update", payload: {}} 

export const increment = createAction("counter/increment"); 
export const decrement = createAction("counter/decrement"); 
export const incrementByAmount = createAction("counter/incrementByAmount"); 
export const sagaAddUser = createAction("saga/addUser"); 
export const sagaTake = createAction("saga/take"); 
export const sagaUpdateUser = createAction("saga/updateUser");

主角之二:createReducer

如果像之前那样要写switch case去匹配action.type其实是比较繁琐的,createReducer可以解决这个问题

createReducer函数接收两个参数,第一个参数为模块初始值state,第二个参数可以使用builder方式处理case reducer

比如下方的count模块的例子:

将reducer需要处理的action对应的函数用builder.addCase的方式挂载来监听;

// store/modules/createReducer.js
import { createReducer } from "@reduxjs/toolkit";
import { increment, decrement, incrementByAmount } from "./action";
// state初始值
const initialState = { value: 0 };

// createAction 运用toString()
const reducers = {
  // 下面两种都行
  [incrementByAmount]: (state, action) => {
    console.log("incrementByAmount", action);
    state.value += action.payload;
  },
  /* [incrementByAmount](state, action) {
    console.log("incrementByAmount", action);
    state.value += action.payload;
  }, */
};

// createReducer的第一个参数为第一次调用reducer时的初始状态
export const counterReducer = createReducer(initialState, (builder) => {
  builder
    .addCase(increment, (state, action) => {  // increment 这里换成 "counter/increment" 是一样的
      console.log("increment", action);  //  若是dispatch(increment({}))  则这里打印:{type: "counter/increment", payload: {}}
      state.value++;  // 看到这个是否有疑问,之前不是说不能直接修改state值吗,只能用return的方式;其实这里是工具包里面做了处理;
    })
    .addCase(decrement, (state, action) => {
      console.log("decrement", action);
      state.value--;
    })
    /* .addCase("counter/incrementByAmount", (state, action) => {
      console.log("incrementByAmount", action);
      state.value += action.payload;
    }) */
    .addCase(incrementByAmount, reducers[incrementByAmount]);
});
// 这里addCase第一个参数可以使用createAction生成的toString(),也可以使用字符串:"xxxx"

然后将该模块挂载到store之中:

这里为啥是挂载到redux里面的reducers之中,其实是rematch用的init定义的关系,这个之后我们再介绍,总之你先可以理解为这样就挂载上store了;

image.png

image.png

使用counter模块的state和修改state:

// Counter.js
import { useSelector, useDispatch } from "react-redux";
import { increment, incrementByAmount } from "../store/modules/action";
const Counter = () => {
  // 获取counter模块的state
  const data = useSelector((state) => state.counter);
  console.log(data, "data"); // { value: 0 }

  const dispatch = useDispatch();
  const add = () => {
      // dispatch.counter 为undefined 是获取不到的;
    console.log(dispatch.counter); //undefined

    dispatch(increment({})); // 以下为大概步骤解析
      // 首先:increment({}) =》 {type: "counter/increment", payload: {}}
      // 接着:dispatch({type: "counter/increment", payload: {}})
      // 然后会直接调用counterReducer模块中定义的increment函数
  };
  const decrement = () => {
      // counterReducer模块中定义了decrement及其函数,故以下dispatch也是可以直接调用定义的decrement函数
    dispatch({ type: "counter/decrement", payload: {} });
  };
  const add2 = () => {
    dispatch(incrementByAmount(2));
  };
  return (
    <div>
      <section className="posts-list">
        <h2>Counter</h2>
        <div>value: {data.value}</div>
      </section>
      <section>
        ........
      </section>
    </div>
  );
};
export default Counter;

是不是看起来很简单,那继续看下一个。


主角之三:createSlice:

我们每次dispatch(action)时,比如上面的例子,都要先定义action是怎样的,那有没有什么方式可以不用自己直接去定义呢;

createSlice:可以创建一个模块store去使用,可以让你不用定义action,它内部已经帮你生成action type

首先,我们先创建一个store模块posts:

当我们在reducers创建方法addPost时,createSlice内部会自动生成一个同名的 “action creator” 函数,即type: "posts/addPost";

那么为什么是 posts/addPost 不是 另外什么  xxx/addPost呢,其实这个跟该createSlice定义时的name有关;name: "posts",

// store/modules/postsSlice.js
import { createSlice } from "@reduxjs/toolkit";
import { getAgentUserAction } from "./action";
// 定义state的初始值
const initialState = {
  list: [
    { id: "1", title: "First Post!", content: "Hello!" },
    { id: "2", title: "Second Post", content: "More text" },
  ],
  status: "wait",
};
const postsSlice = createSlice({
  name: "posts", // 内部生成action.type的前缀
  initialState, // 相当于postsSlice模块的state初始值
  reducers: { // 定义方法
    addPost(state, action) {  
      console.log(action);  // {type: 'posts/addPost', payload: xxx}
      state.push(action.payload);
    },
  },
});
// 将定义的方法暴露出去
export const { addPost } = postsSlice.actions;

// 当我们编写 addPost reducer 函数时,createSlice 会自动生成一个同名的 “action creator” 函数,即type: "posts/addPost"。
// 这里就可以看出如果在其他文件引入addPost将其调用,则:addPost({}) =》 {type: "posts/addPost", payload: {}}
console.log(postsSlice.actions.addPost({})); // {type: "posts/addPost", payload: {}}

export const selectAllPosts = (state) => state.posts;
export default postsSlice.reducer; // 这里就可以将该模块挂载到store之中;

然后将posts模块挂载到store之中:

这里为啥是挂载到redux里面的reducers之中,其实是rematch用的init定义的关系,这个之后我们再介绍,总之你先可以理解为这样就挂载上store了;

image.png

image.png

那么,我们该如何使用posts的state和更新state呢:

上代码:里面有注释说明

// PostsList.js
import { useSelector, useDispatch } from "react-redux";
import { nanoid } from "@reduxjs/toolkit";
import { addPost, selectAllPosts } from "../store/modules/postsSlice";
import { getAgentUserAction } from "../store/modules/action";

const PostsList = () => {
  const posts = useSelector((state) => state.posts);
  const dispatch = useDispatch();
  console.log(posts, "posts"); // 这里就可以取得posts模块的所有state了

  const save = () => {
    const params = { id: nanoid(), title, content };
    console.log(addPost, "addPost"); // ƒ posts/addPost "addPost"
    // addPost(params) => {type: "posts/addPost", payload: params}
	// 可以看出addPost跟createAction创建出来的是一样的,只不过是内部做了处理;const addPost = createAction('posts/addPost')
    
    dispatch(addPost(params));
   // 首先 addPost(params) => {type: "posts/addPost", payload: params} => 就是相当于返回一个action
    // 然后 dispatch({type: "posts/addPost", payload: params})
    // 相当于调用posts中的addPost方法:addPost(state, action); action等于 {type: "posts/addPost", payload: params}
   // 另外:dispatch.posts 为undefined 是获取不到的;
    // 其实你直接dispatch(addPost(params)); 换成 dispatch({type: "posts/addPost", payload: params}) 也是没问题可以触发的
   console.log(dispatch.posts, "dispatch.posts");
  };

  return (
    <div>
      <section className="posts-list">
        <h2>Posts</h2>
      </section>
      ........
    </div>
  );
};

export default PostsList;

这个时候你会发现我们定义的方法是为同步的,那要是我想处理异步逻辑该怎么办呢,其实有个字段extraReducers可以使用

详情可以参考这里→ cn.redux.js.org/tutorials/e…   (主要是自己没有去动手操作只是看了下😅)


在这里穿插介绍一个功能的使用:bindActionCreators

import { bindActionCreators } from "redux";
import { sagaAddUser } from "../store/modules/action";

bindActionCreators(sagaAddUser, dispatch)("张三");  // 其实就是=》 dispatch(sagaAddUser("张三")); 
// bindActionCreators(sagaAddUser, dispatch) // 返回的是一个包裹dispach的函数

// let fn = bindActionCreators(sagaAddUser, dispatch) 就是等于 fn = (id) => dispatch(sagaAddUser(id))
// 所有fn("张三") 就是等于 fn = ("张三") => dispatch(sagaAddUser("张三"))

这一篇的分享就到这里结束了,那有没有其他方法去处理异步逻辑呢,这个时候redux-saga中间件就可以派上用场了,若有兴趣了解redux-saga可看下一篇哦。