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))}
>
×
</button>
</li>
);
});
return (
<div>
<ul style={{ marginTop: 20 }}>{lis}</ul>
</div>
);
}
export default List;