目录
介绍
演示
异步action
react-redux
多组件管理的react-redux
扩展
介绍
redux是专门用于集中式管理状态的javascript库,并不是react的插件库。
比如你有多个组件A-E都想要用同一个组件D中的状态:
1)像以前我们可以通过父子组件通信的方式让父组件进行传递状态,或者是让兄弟组件之间通过订阅发布进行通信
2)当我们使用了redux就可以直接通过让redux进行统一的状态管理,谁想要用状态就自己去拿,省去了第一种方法的层层传递
redux三个核心概念
action
动作的对象
包含两个属性
type: 标识属性 值为字符串 唯一 必要属性
data: 数据属性 值类型任意 可选属性
reducer
用于初始化状态 加工状态
加工时 根据旧的state和action 产生新的state的纯函数
store
将state action reducer联系在一起的对象
该对象api
(1) getState()得到state
(2) dispatch(action): 分发action 触发reducer调用 产生新的state
(3) subscribe(listener) 注册监听 当产生了新的state时 自动调用
演示
首先安装
npm i redux
准备项目结构
书写一个加减法案例
创建reducer文件
创建store
调用时分配的type值要和reducer中定义的type类型一致
注意这里的
store.subscribe订阅,这只是订阅了当前count单个组件
当使用了几十上百的组件让redux管理状态时,就需要在每个组件里都书写这个订阅,这样是很不方便的,所以可以直接在index.js主文件中直接书写一个全局订阅所有组件
上面示例还可以进行细致拆分
新建一个action文件来进行简单管理action
新建action文件
使用action
还可以将type的值进行封装,这样可以避免因为疏忽造成单词拼错的错误,并且后期维护更改只需更改封装的一处地方即可
新建contant文件
使用:
这样以后想要更改常量type的各种枚举值时只需更改contant文件即可
异步action
在刚才的例子中书写异步加时是在count组件中定义一个异步任务去调用加 的方法的
并不是真正的异步action
redux需要中间件redux-thunk的配合才能定义异步aciton
安装中间件
npm i redux-thunk
使用
store文件引入
调用
react-redux
react官方还提供了一个react-redux的插件库用来对redux进行解耦,使得我们在使用组件时可以隔绝组件直接和redux进行交互,组件只需要通过父组件的props进行一系列的操作,而和redux的交互全部交给父组件来进行处理 具体原理图如下
安装react-redux
npm i react-redux
创建结构
创建父子组件连接
子组件使用
可以看到子组件的引入中没有原先的store和action文件了,将store和action等进行了一系列的解耦
查看当前对象打印
其中容器组件中的mapStateToProps和mapDispatchToProps还可以进行一定程度的简写
第一步简写:
第二步简写:
第三步简写:
react-redux 的优化
之前在使用纯redux时,redux的状态改变时我们需要手动添加redux的状态订阅从而重新渲染组件
react-redux对这一功能进行了优化,我们只要使用了react-redux,它内部已经封装好了订阅功能,这里不需要我们再进行手动的订阅了
测试页面还是正常的,这里不做页面测试的展示了
在App.js文件中引入了容器组件,如果这里的容器组件有几十上百个时候,我们就需要每个都手动添加store={store},react-redux对此点也进行了优化
首先删除掉每个容器组件的store={store}
在引入<App>组件的文件中引入react-redux的api: Provider
直接包裹使用,这样以后不管有多少容器组件,都不需要我们再一个一个手动添加store={store}了
为了避免以后组件和容器组件过多,比如有10个组件的话,就需要再书写10个容器组件来对原组件进行包裹,这样产生的文件可能会冗余,其实可以直接将容器组件和组件进行合并为一个文件
下面给出一个合并例子
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'
import { createJiaAction, createJiaAsyncAction, createJianAction } from '../../redux/count_action'
import React, { Component } from 'react'
class Count extends Component {
// componentDidMount(){
// //store订阅组件状态更新 每当状态值更新时就去重新渲染
// store.subscribe(()=>{
// this.setState({})
// })
// }
jia=()=>{
//拿取当前下拉值
const {value} = this.selectNumber
//value*1 是将字符串类型数字转数字 也可写为value-0
this.props.jia(value*1)
}
jian=()=>{
//拿取当前下拉值
const {value} = this.selectNumber
//value*1 是将字符串类型数字转数字 也可写为value-0
this.props.jian(value-0)
}
jiaIfOdd=()=>{
const {value} = this.selectNumber
if(this.props.count %2 ===1){
this.props.jia(value-0)
}
}
jiaAsync=()=>{
const {value} = this.selectNumber
// setTimeout(()=>{
// store.dispatch(createJiaAsyncAction(value*1,1500))
// },1000)
this.props.asyjia(value-0,1000)
}
render(){
console.log('当前对象')
console.log(this)
return (
<div>
<h1>当前求和值为: {this.props.count}</h1>
<select ref={c => this.selectNumber = c}>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
<option value='4'>4</option>
</select>
<button onClick={this.jia}>+</button>
<button onClick={this.jian}>-</button>
<button onClick={this.jiaIfOdd}>当前求和为奇数时加</button>
<button onClick={this.jiaAsync}>异步任务加</button>
</div>
)
}
}
//mapStateToProps函数返回的对象中的key作为传递给UI组件的key
//value作为出传递给UI组件的pros的value-状态值
// function mapStateToProps(state){
// return {count:state}
// }
//简写
// const mapStateToProps = state => ({count:state})
// mapDispatchToProps函数返回的对象结构为: {key:function}
//对象中的key作为传递给ui组件props的key
//value作为传递给ui组件props的value-操作状态的方法
// function mapDispatchToProps(dispatch){
// return {
// jia:number =>dispatch(createJiaAction(number)),
// //上一行写法等价于
// jian: (number)=>{return dispatch(createJianAction(number))},
// asyjia: (number,time)=>dispatch(createJiaAsyncAction(number,time))
// }
// }
// 简写
// const mapDispatchToProps = (dispatch)=>({
// jia:number =>dispatch(createJiaAction(number)),
// //上一行写法等价于
// jian: (number)=>{return dispatch(createJianAction(number))},
// asyjia: (number,time)=>dispatch(createJiaAsyncAction(number,time))
// })
//使用connect()()创建并暴露一个Count的容器组件
// export default connect(state => ({count:state}),(dispatch)=>({
// jia:number =>dispatch(createJiaAction(number)),
// //上一行写法等价于
// jian: (number)=>{return dispatch(createJianAction(number))},
// asyjia: (number,time)=>dispatch(createJiaAsyncAction(number,time))
// }))(CountUi)
export default connect(state => ({count:state}),
{
jia:createJiaAction,
jian:createJianAction,
asyjia:createJiaAsyncAction
}
)(Count)
直接将组件书写在容器组件中,但是组件不再进行暴露了,而容器组件也不需要再去引入组件了
多组件管理的react-redux
前面的例子是单个容器组件的react-redux管理,如果是多个组件需要使用react-redux进行数据共享呢
下面再新建一个person组件来进行演示,并新建属于person的action和reducer
将各属于count,person的action, reducer再次进行结构的细分
添加新的常量
编写person的action
编写person的reducer
此时之前书写的store需要一些调整,之前只有count组件时,store文件里只引入了count组件的reducer进行服务,而这里新加了Person组件,所以store文件中需要再引入person组件的reducer进行对Person组件服务
书写Person组件
这里采用容器组件和组件合并的形式
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'
//引入迷你版uid生成器
import {nanoid} from 'nanoid'
import { addObj } from '../../redux/actions/person/action'
import React, { Component } from 'react'
class Person extends Component {
addObj = ()=>{
let uName = this.uName.value
let uAge = this.uAge.value
let perObj = {id:nanoid(),uName,uAge}
this.uName.value = ''
this.uAge.value = ''
this.props.jiaPer(perObj)
}
render(){
console.log('person组件的this')
console.log(this.props)
return (
<div>
<h1>Person组件</h1>
<h2>求和结果为{this.props.resultHe}</h2>
<input ref={c => this.uName = c} placeholder='请输入名字'/>
<input ref={c => this.uAge = c} placeholder='请输入年龄'/>
<button onClick={this.addObj}>点击添加</button>
<ul>
{
this.props.perObjs.map(item=>{
return (
<li key={item.id}>名字---{item.uName},年龄--{item.uAge}</li>
)
})
}
</ul>
</div>
)
}
}
export default connect(state => ({perObjs:state.pers,resultHe:state.he}),
{
jiaPer: addObj //映射 action中的person
}
)(Person)
这个地方有些绕,要深入的理解下store的使用方法
countReducer和PeronReducer中的方法返回的最终数据都会被绑定在store的state中,而在store中又封装了一个总的reducer对象来将两个文件返回的数据作为value绑定在了
{
he:countReducer,
pers:personReducer
}
所以取值时就是直接state(相当于直接调用store.getState()方法).he或者state.pers了
在person组件中取得count组件的求和值
在count组件中取得person组件的人数值从而实现两个组件互相取得对方的数据实现数据共享
测试
测试可以看到count组件拿取到了person组件的人数,person组件也拿到了count组件的求和数,从而两个组件借助react-redux实现了数据共享
安装redux开发者工具
在谷歌开发者商店里搜索redux devtools下载插件进行安装
或者直接在下面链接中下载安装
链接:react全家桶资料
在浏览器插件安装中选择该文件夹即可
该插件需要在react项目中配合第三方库进行使用
安装第三方库
npm i redux-devtools-extension
重启项目
可以看到该插件图表亮了,点击后唤出操作页面,该插件可以对redux中的state的变化进行可视化的监听,具体操作这里不再演示
扩展
纯函数
(1) 一类特别的函数: 只要是同样的输入(实参) 必定得到同样的输出(返回)
(2) 必须遵以下约束:
1) 不得改写参数数据
2) 不会产生任何副作用 例如网络请求 输入和输出设备
3) 不能调用Date.now()或者Math.random()等不纯的方法
(3) redux的reducer函数必须是一个纯函数