一、redux介绍
Redux——状态管理器
对于大型项目来说,每个组件有自己的state,组件数量庞大时,组件间通信复杂,redux的思想是将各组件的state统一管理。
Redux可以理解是reducer和context的结合体,使用Redux既可以实现管理复杂的state,又可以在不同的组件之间方便共享传递state。Redux主要应用于大型的应用
。
解决组件间通信的问题,将要共享的状态数据,存放在redux中——集中式
二、Redux原理图:(旧版本)
(1)Action Creators:服务员用点菜宝生成菜单,将动作分发给store
创建一个Action动作对象,包含两个属性——type和data,需要dispatch分发动作才会有后续,dispatch(action)
(2)Store:老板接过菜单,传给后厨
仓库,调度者,指挥者(C位,将state、action、reducer联系在一起)
(3)Reducers:勤恳的后厨,真正执行动作der
用于初始化状态、加工状态,根据传过来的action和previousState,返回一个新的state数据。
三、react中redux应用
Step1:安装包 @reduxjs/toolkit
Step2:src/store/slicerModules/counterSlicer.js 在该文件中创建切片对象counterSlicer
//导入包
import { createSlice } from '@reduxjs/toolkit';
//创建切片对象(实际可以理解成仓库中的货架)
const counterSlicer = createSlice({
//用来自动生产action中的type属性
name: 'counterSlicer',
// 定义初始状态数据(当前切片的state的初始值)
// 这个属性值类型必须为一个对象(Object/Array)
initialState: {
num: 100
},
// 指定state的各种操作/动作方法
reducers: {
//添加动作action
//add:function(){} 此处为ES6中对象的方法的简写
//每个动作中传递两个参数:
//第一个参数:state: 表示仓库中的状态数据(就是initialState中的数据值)
//第二个参数:action:表示动作对象,其中的payload对我们有用,直接解构
add(state, { payload }) {
console.log(state);//状态数据,返回一个对象,已加密处理,代理对象。可以直接修改state
console.log(action);//{type: 'count/add', payload: undefined}
//修改状态数据
state.num += payload;
}
//在调用动作方法时,传的实参就是payload的值
}
/*counter切片对象:
1、actions属性: {add: ƒ}
//actions属性中保存了所有的动作方法(action创建器),调用函数会自动创建action对象
//action对象的结构:{type: 'name/动作方法名', payload: xxx}
//调用动作方法时,传入的实参,就是payload的值
2、caseReducers属性: {add: ƒ}
3、getInitialState属性:ƒ ()
4、name属性: "count"
5、reducer属性: ƒ (state, action)
*/
})
//因为在组件中需要调用修改状态值的方法
//暴露add方法,从Counter切片对象的actions属性中,解构出add方法
//切片对象的actions属性中,存着切片对象的所有动作方法
export let { add } = Counter.actions;
export default counterSlicer ;
【注】创建切片对象后,自动生成reducer和actions:
actions用于解构出来动作方法;reducer用于创建store对象的配置字段
Step3:src/store/index.js 在该文件中创建store仓库对象
//1、导入包
import {configureStore} from '@reduxjs/toolkit';
//2、导入切片对象(默认方式的暴露,对应默认引入)
import counterSlicer from './slicerModules/counterSlicer';
//3、创建仓库对象
//configureStore方法需要传递一个配置对象,createSlice方法也是
let store = configureStore({
//当存在多个reducer时,传入一个对象
reducer:{
counterSlicer:counterSlicer.reducer
}
})
//4、暴露store仓库对象(在组件中会使用到)
export default store;
Step4:src/Component/Counter/Counter.jsx(组件文件)
//Redux组件的文件
import React from 'react'
//默认导出的 导入
import store from '../../store/index'
//分别导出的 导入
import { add } from '../../store/slicerModules/counterSlicer'
//规定userSelector函数中需要传递一个回调函数
//回调函数中的参数表示仓库中的所有的状态数据(state)
//useDispatch钩子返回的是一个dispatch函数,所以在后期做动作的分发的时候可以不需要仓库对象
import { useDispatch, useSelector } from 'react-redux'
export default function Redux() {
let dispatch = useDispatch();
//这里的counterSlicer是在store配置对象中的reducer的命名
//写法一:二级解构
let { counterSlicer: { num } } = useSelector(state=>state);
//写法二:返回状态数据时,直接返回某一个切片对象的
let num = useSelector(state=>state.counterSlicer);
let AddCount = (n) => {
//修改状态数据,一定需要用到dispatch方法
//add方法中的实参n就是将来所需要的载荷数据(俗称变化量)
dispatch(add(n))
}
return (
<div>
<p>计数器:{num}</p>
<p><button onClick={() => { AddCount(100) }}>+100</button></p>
</div>
)
}
Step5:在入口文件index.js中,使用react-redux包实现自动更新
React-Redux的使用:
Redux存在的问题:需要自己绑定监听来重新渲染整个组件,每次state更新, 都需要重新render所有组件(使用subscribe方法进行订阅redux状态的更新) ,
需要下载react-redux的包/插件
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './store/index'
import { Provider } from 'react-redux';
const root = ReactDOM.createRoot(document.getElementById('root'));
//根据仓库中的状态数据的变化,进行订阅
//让App下的所有的组件进行重新渲染最新的状态数据
root.render(
//Provider组件要求传入一个store属性
<Provider store={store}>
<App />
</Provider>
);
//Redux组件的文件
import React from 'react'
// import store from '../../store/index'
import { add } from '../../store/modules/counter'
import { useSelector, useDispatch } from 'react-redux'
//useSector钩子函数,用于加载state中的数据
export default function Redux() {
//规定userSelector函数中需要传递一个回调函数
let { count: { num } } = useSelector(state => state);
let dispatch = useDispatch();
let AddCount = (n) => {
// store.dispatch(add(n));
dispatch(add(n));
}
return (
<div>
<p>计数器:{num}</p>
<p><button onClick={() => { AddCount(100) }}>+100</button></p>
</div>
)
}
如果有异步代码,要在切片对象中单独定义并暴露,异步代码返回一个dispatch函数(函数的形参是patch),要在函数体中写异步代码,调用dispatch,且要调用上边的同步代码。
//切片对象文件
export let asyncAddCounter = payload => {
//返回一个dispatch函数
return dispatch => {
//异步代码(定时器、ajax请求)
setTimeout(() => {
//调用dispatch,参数是一个同步的方法
//给同步方法传参时,具体参照同步方法定义时,需要用到什么样的参数
dispatch(add(payload));
}, 1000)
}
}
//调用切片对象的那个组件文件
import React from 'react'
// import store from '../../store/index'
import { asyncAddCounter } from '../../store/modules/counter'
import { useSelector, useDispatch } from 'react-redux'
export default function Redux() {
...
let asyncAddCountfun= (n) => {
//异步的动作方法也需要dispatch下发动作
dispatch(asyncAddCounter(n));
}
return (
<div>
<p>计数器:{num}</p>
<p><button onClick={() => { AddCount(100) }}>+100</button></p>
<p><button onClick={() => { asyncAddCountfun(10) }}>异步+10</button></p>
</div>
)
}