第7章:redux
7.1. redux理解
7.1.1. 学习文档
- 英文文档: https://redux.js.org/
- 中文文档: http://www.redux.org.cn/
- Github: https://github.com/reactjs/redux
下载redux和开发者工具
npm install --save react-redux
npm install --save-dev redux-devtools
7.1.2. redux是什么
- redux是一个专门用于做状态管理的JS库(不是react插件库)。
- 它可以用在react, angular, vue等项目中, 但基本与react配合使用。
- 作用: 集中式管理react应用中多个组件共享的状态。
7.1.3. 什么情况下需要使用redux
- 某个组件的状态,需要让其他组件可以随时拿到(共享)。
- 一个组件需要改变另一个组件的状态(通信)。
- 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。
7.1.4. redux工作流程
7.2. redux的三个核心概念
7.2.1. action
- 动作的对象
- 包含2个属性
- type:标识属性, 值为字符串, 唯一, 必要属性
- data:数据属性, 值类型任意, 可选属性
- 例子:{ type: ‘ADD_STUDENT’,data:{name: ‘tom’,age:18} }
7.2.2. reducer
- 用于初始化状态、加工状态。
- 加工时,根据旧的state和action, 产生新的state的纯函数。
7.2.3. store
- 将state、action、reducer联系在一起的对象
- 如何得到此对象?
- import {createStore} from ‘redux’
- import reducer from ‘./reducers’
- const store = createStore(reducer)
- 此对象的功能?
- getState(): 得到state
- dispatch(action): 分发action, 触发reducer调用, 产生新的state
- subscribe(listener): 注册监听, 当产生了新的state时, 自动调用
7.3. redux的核心API
7.3.1. createstore()
作用:创建包含指定reducer的store对象
7.3.2. store对象
- 作用: redux库最核心的管理对象
- 它内部维护着:
- state
- reducer
- 核心方法:
- getState()
- dispatch(action)
- subscribe(listener)
- 具体编码:
- store.getState()
- store.dispatch({type:‘INCREMENT’, number})
- store.subscribe(render)
7.3.3. applyMiddleware()
作用:应用上基于redux的中间件(插件库)
7.3.4. combineReducers()
作用:合并多个reducer函数
7.4. 使用redux编写应用
- 求和案例_redux精简版
-
去除Count组件自身的状态
-
src下建立:
- -redux
- -store.js
- -count_reducer.js
- -redux
-
store.js:
- 引入redux中的createStore函数,创建一个store
- createStore调用时要传入一个为其服务的reducer
- 记得暴露store对象
// store.js import {createStore} from 'redux' import countReducer from './count_reducer' const store = createStore(countReducer) export default store
-
count_reducer.js:
- reducer的本质是一个函数,接收: preState,action,返回加工后的状态
- reducer有两个作用:初始化状态,加工状态
- reducer被第一次调用时,是store自动触发的
- 传递的preState是undefined
- 传递的action是:{type:’@@REDUX/INIT_a.2.b.4’}
// count_reducer.js const initState = 0 export default function countReducer(preState=initState, action) { const {type,data} = action switch(type) { case 'increment': return preState + data case 'decrement': return preState - data default: return preState ; } }
-
在index.js中检测store中状态的改变,一旦发生改变重新宣染。
备注: redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
-
- 求和案例_redux完整版新增文件:
- count_action.js专门用于创建action对象
import {INCREMENT,DECREMENT} from './constant' export function createIncrementAction(data) { return {type:INCREMENT,data} } // 表达式返回值用小括号包裹 export const createDecrementAction = data => ({type:DECREMENT,data})
- constant.js放置由于编码疏忽写错action中的type,便于后期维护
// 定义action中type类型的常量值 export const INCREMENT = 'increment' export const DECREMENT = 'decrement'
7.5. redux异步编程
7.5.1理解:
- redux默认是不能进行异步处理的,
- 某些时候应用中需要在redux中执行异步任务(ajax, 定时器)
7.5.2. 使用异步中间件
npm install --save redux-thunk
7.5.3. 求和案例_redux异步action版
- 明确:延迟的动作不想交给组件自身,想交给action。
- 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
- 具休编码:
- yarn add redux-thunk,并配置在store中
- 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
- 异步任务有结果后,分发一个同步的action去真正操作数据。
- 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。
store.js
import {createStore,applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import countReducer from './count_reducer'
const store = createStore(countReducer,applyMiddleware(thunk))
export default store
count_action.js
import {INCREMENT,DECREMENT} from './constant'
export const createIncrementAction = data => ({type:INCREMENT,data})
// 表达式返回值用小括号包裹
// 同步action,就是指返回值为对象
export const createDecrementAction = data => ({type:DECREMENT,data})
// 异步action,就是指返回值为函数,异步action中一般都会调用同步action
export const createIncrementAsyncAction = (data,time) => {
return (dispatch)=>{
setTimeout(()=>{
dispatch(createIncrementAction(data))
},time)
}
}
7.6. react-redux
7.6.1. 理解
- 一个react插件库
- 专门用来简化react应用中使用redux
7.6.2. react-Redux将所有组件分成两大类
-
UI组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 通过props接收数据(一般数据和函数)
- 不使用任何 Redux 的 API
- 一般保存在components文件夹下
-
容器组件
- 负责管理数据和业务逻辑,不负责UI的呈现
- 使用 Redux 的 API
- 一般保存在containers文件夹下
7.6.3. 相关API
-
Provider:让所有组件都可以得到state数据
-
connect:用于包装 UI 组件生成容器组件
-
mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性
-
mapDispatchToProps:将分发action的函数转换为UI组件的标签属性
7.6.4. react-redux优化
- 容器组件和UI组件混成一个文件
- 无需自己给容器组件传递store,给包裹一个即可。
- 使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。
- mapDispatchToProps也可以简单的写成一个对象。
- 一个组件要和redux“打交道”要经过哪几步?
- 定义好UI组件-不暴露
- 引入connect生成一个容器组件,并暴露,写法如下:
connect( state => ({key:value}) // 映射状态 {key:xxxxxAction} // 映射操作状态的方法 )(UI组件)
- 在UI组件中通过this.props.xxxxxxx读取和操作状态
container/count/index.jsx
// 容器组件
import {connect} from 'react-redux'
import {createIncrementAction,createDecrementAction,createIncrementAsyncAction} from '../../redux/count_action'
import React, { Component } from 'react';
class Count extends Component {
state = {}
// 加法
increment = ()=> {
const {value} = this.selectNumber
this.props.jia(value*1)
}
// 减法
decrement = ()=> {
const {value} = this.selectNumber
this.props.jian(value*1)
}
// 奇数再加
incrementOfOdd = ()=> {
const {value} = this.selectNumber
if(this.props.count % 2 !==0) {
this.props.jia(value*1)
}
}
// 异步加
incrementAsync = ()=> {
const {value} = this.selectNumber
this.props.jiaAsync(value*1,500)
}
render() {
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>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementOfOdd}>奇数再加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
);
}
}
export default connect(
state => ({count:state}),
{
jia:createIncrementAction,
jian:createDecrementAction,
jiaAsync:createIncrementAsyncAction
}
)(Count)
7.6.5. 数据共享
- 定义一个Pserson组件,和Count组件通过redux共享数据。
- 为Person组件编写: reducer、action,配置constant常量。
- 重点: Person的reducer和ICount的Reducer要使用combineReducers进行合并,合并后的总状态是一个对象!!!
- 交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。
store.js
import {createStore,applyMiddleware,combineReducers} from 'redux'
import thunk from 'redux-thunk'
import countReducer from './reducers/count'
import personReducer from './reducers/person'
// 汇总
const allReducer = combineReducers({
he: countReducer,
ren: personReducer
})
const store = createStore(allReducer,applyMiddleware(thunk))
export default store
7.7. 使用上redux调试工具
7.7.1. 安装chrome浏览器插件
7.7.2. 下载工具依赖包
npm install --save-dev redux-devtools-extension
引用
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))