十、React-Redux介绍


React-redux 介绍

Redux 是一个独立的第三方库,之后 React 官方在 Redux 的基础上推出了 _React-redux_:https://react-redux.js.org/

最新版的 React-redux ,已经全面拥抱了 Hooks ,内置了诸如:

  • useSelector
  • useDispatch
  • useStore

一类的 Hook ,我们只要掌握这一类 Hook ,就可以轻松上手。

另外,Redux 官方还推出了 Redux Toolkit ,来简化整个 Redux 的使用。官方文档:https://redux-toolkit.js.org/

因此现在在 React 应用中,状态管理库的使用一般都是 React-redux + Redux Toolkit

React-redux 示例:ToDoList

首先第一个安装两个依赖,命令如下:

npm install @reduxjs/toolkit react-redux

好了我们开始重构刚才的项目

Redux 目录中的 4 个文件会直接简化为 2 个,有些东西不需要我们再写了,会有 toolkit 自动帮我们生成。


store.js 的变化,从 toolkit 里面引入 configureStore 方法,用于创建我们的数据仓库,返回了一个store,所以在index.js中引入时直接引入了一个store

// 引入创建仓库的方法
import { configureStore } from "@reduxjs/toolkit";
import todolistReducer from "./todolistSlice";

// 调用该方法时,传入一个配置对象
// 其中一个选项是配置 reducer
export default configureStore({
  reducer : {
    todo : todolistReducer
  }
});

todolistSlice.js,值得注意的是这里定义的时候是reducers,导出的时候使用的是reduce,同时还导出了相关action

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

export const todolistSlice = createSlice({
  // 切片的命名空间
  name: "todolist",
  // 初始化仓库数据
  initialState: {
    list: [
      {
        content: "学习 React",
        status: false,
      },
      {
        content: "复习 Vue",
        status: false,
      },
      {
        content: "玩游戏",
        status: false,
      },
      {
        content: "听歌",
        status: false,
      },
    ],
  },
  // reducers
  reducers: {
    /**
     *
     * @param {*} state 上一次的仓库数据
     * @param {*} param1 传递过来的数据
     */
    add: (state, { payload }) => {
      // 允许你的直接修改 state
      // 如果你去阅读 redux-toolkit 的源码
      // 你会发现底层使用到了 immer.js 这个库
      state.list.push({
        content: payload,
        status: false,
      });
    },
    del: (state, { payload }) => {
      state.list.splice(payload, 1);
    },
    change: (state, { payload }) => {
      state.list[payload].status = !state.list[payload].status;
    },
  },
});

// 可以打印看下返回的这个todolistSlice是什么
// {
//   actions: { add: f, del: f, change: f }
//   caseReducers: { add: f, del: f, change: f }
//   name: 'todolist',
//   reducer: f (state, change)
// }

// action 之前是通过我们自己书写的 action creator 来创建的,现在是直接从 slice 里面导出即可。
export const { add, del, change } = todolistSlice.actions;

export default todolistSlice.reducer;

index.js 的变化,需要从 react-redux 中引入 Provider 的组件,用于提供一个上下文环境,包裹应用的根组件,之后仓库会做为 Provider 的 store 属性,不需要再在 App.jsx 根组件上面挂载了

// ....
import { Provider } from "react-redux";

// 引入仓库
import store from "./redux/store";

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(
  <Provider store={store}>
    <App />
  </Provider>
);

我们来看看app.jsx,组件不需要父级一级一级的传递store了,那么子组件怎么访问呢,我们继续来看

import Input from "./components/Input";
import List from "./components/List";

import "./css/App.css"

function App(props) {

  return (
    // 最外层容器
    <div className="container">
      <h1 className="lead" style={{
        marginBottom : "30px"
      }}>待办事项</h1>
      <Input/>
      <List/>
    </div>
  )

}

export default App;

list.jsx,组件连接仓库的改变,之前使用 redux 的时候,组件还是需要从父组件传递的 props 上面拿到仓库数据,现在可以通过 useSelector 这个 Hook 直接连接仓库,并且通过useDispatch以及导入的action实现数据状态的变更,注意这里为什么是const { list } = useSelector((state) => state.todo),这个是我们在store.js导出时候定义的,可以回去看。

import React from "react";
import { useSelector, useDispatch } from "react-redux";
// action 之前是通过我们自己书写的 action creator 来创建的,现在是直接从 slice 里面导出即可。
import { del, change } from "../redux/todolistSlice";

function List() {
  const { list } = useSelector((state) => state.todo);
  const dispatch = useDispatch();

  const lis = list.map((item, index) => {
    return (
      <li key={index} className="text-primary">
        <span
          // 直接change(index)肯定不对的,参数都对不上,需要dispatch转发一下
          onClick={() => dispatch(change(index))}
          className={["item", item.status ? "completed" : ""].join(" ")}
        >
          {item.content}
        </span>
        <button
          type="button"
          className="close"
          onClick={() => dispatch(del(index))}
        >
          &times;
        </button>
      </li>
    );
  });

  return (
    <div>
      <ul style={{ marginTop: 20 }}>{lis}</ul>
    </div>
  );
}

export default List;

文章作者: 吴俊杰
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 吴俊杰 !
  目录