[React] 12 - Redux: async & middleware, container component
这里只是简单地了解中间件的概念,对于异步,貌似之后要讲的saga更胜一筹。
reducer计算新状态的策略:
- Action 发出以后,Reducer 立即算出 State,这叫做同步;
- Action 发出以后,过一段时间再执行 Reducer,这就是异步。
Action 也可以是 Function
中间件
需要有个中间件提供帮助,故安装:redux-thunk,乃”形实转换程序“。
store.js
//引入createStore,专门用于创建redux中最为核心的store对象 import {createStore, applyMiddleware} from 'redux'
//引入为Count组件服务的reducer import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action import thunk from 'redux-thunk'
//暴露store export default createStore(countReducer, applyMiddleware(thunk))
如下,大括号中成了函数;而小括号中则是对象。
异步情况下,action 的定义中是个”返回的异步函数“。
count_action.js
import {INCREMENT,DECREMENT} from './constant' //同步action,就是指action的值为Object类型的一般对象 export const createIncrementAction = data => ({type:INCREMENT,data}) export const createDecrementAction = data => ({type:DECREMENT,data}) //异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。 export const createIncrementAsyncAction = (data, time) =>{ return (dispatch)=>{ setTimeout(()=>{ dispatch(createIncrementAction(data)) },time) } }
上面的dispatch 成了参数(固定写法),因为里面肯定需要引用store,干脆默认通过传过来即可。
再可见,下面的setTimeout这个异步操作转移到了action部分。是否异步,应该在action中体现;当然,下面的”原来的写法“也可以达到同样效果。
./components/Count
//加法 increment = ()=>{ const {value} = this.selectNumber store.dispatch(createIncrementAction(value*1)) } //异步加(转移到action后,与上面的形式就一样了~) incrementAsync = ()=>{ const {value} = this.selectNumber // setTimeout(()=>{ store.dispatch(createIncrementAsyncAction(value*1, 500)) // },500) }
React-redux 模型图
Container and UI
UI 组件不与 redux 直接通信,也不使用redux API。以下是组件的父子关系图。
添加 容器组件
在 ./components/Count 中,可见这个UI组件中包含了 redux 的东西,我们希望分离开来。
- 第一步,从”上层“ 给 容器组件 传入 store
App.jsx(主组件)
import React, { Component } from 'react' import Count from './containers/Count' import store from './redux/store' export default class App extends Component { render() { return ( <div> {/* 给 “容器组件” 传递 “store”,两者粘在了一起 */} <Count store={store} /> </div> ) } }
- 第二步,容器组件 引入 UI组件
Goto: https://youtu.be/YRj1LMn0Rwc?t=1334
./containers/Count/index.jsx
//引入Count的UI组件 import CountUI from '../../components/Count' //引入connect用于连接UI组件与redux import {connect} from 'react-redux' //使用connect()()创建并暴露一个Count的容器组件 export default connect()(CountUI) // --> 该 容器组件 引入 了这个UI组件:CountUI
In a nutshell, Store --> (容器组件) --> UI 组件
(过去如下,CountUI 对应的 Count 组件 在 主组件 下调用。这本就是默认的react套路)
App.js
import React, { Component } from 'react' import Count from './components/Count' export default class App extends Component { render() { return ( <div> <Count/> </div> ) } }
完善 Container Componient
Ref: 105 尚硅谷 react教程 react redux基本使用
-
为了 传递参数 to "UI组件"
因为 App.js中已经传入了 store,所以以下代码可以不再提及store,但实在已经有了store这个全局对象环境 as default。
./containers/Count/index.jsx
//引入Count的UI组件 (为了粘和) import CountUI from '../../components/Count' //引入action(为了粘和) import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' //引入connect用于连接UI组件与redux import {connect} from 'react-redux'
/* **************************************************************************** 1.mapStateToProps函数返回的是一个对象; 2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value 3.mapStateToProps用于传递 "状态" */
// (props), 可以稍微优化 function mapStateToProps(state){ return {
count:state // <---- store.getState()
} }
// *** 原本是这样子:return {count:store.getState()},简化后就是上面这样子 *** /* **************************************************************************** 1.mapDispatchToProps函数返回的是一个对象; 2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value 3.mapDispatchToProps用于传递 "操作状态的方法" */
// (props),可以优化到“点到为止” function mapDispatchToProps(dispatch){ return { jia:number => dispatch(createIncrementAction(number)), // dispatch发送的是一个action's object. jian:number => dispatch(createDecrementAction(number)), jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number, time)), } }
// 使用connect()()创建并暴露一个Count的容器组件 export default connect(mapStateToProps, mapDispatchToProps)(CountUI)
dispatch之所以能 调度到 countReducer,因为store在此定义。
//引入createStore,专门用于创建redux中最为核心的store对象 import {createStore,applyMiddleware} from 'redux' //引入为Count组件服务的reducer import countReducer from './count_reducer' //引入redux-thunk,用于支持异步action import thunk from 'redux-thunk' //暴露store export default createStore(countReducer,applyMiddleware(thunk))
//同步action,就是指action的值为Object类型的一般对象 export const createIncrementAction = data => ({type:INCREMENT,data}) export const createDecrementAction = data => ({type:DECREMENT,data}) //异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。 export const createIncrementAsyncAction = (data,time) => { return (dispatch)=>{ setTimeout(()=>{ dispatch(createIncrementAction(data)) },time) } }
action's object 发送到这里 进行实际操作。
./redux/count_reducer.js
import {INCREMENT,DECREMENT} from './constant' const initState = 0 //初始化状态 export default function countReducer(preState=initState, action){ // console.log(preState);
//从action对象中获取:type、data const {type, data} = action switch (type) { case INCREMENT: // 每个 action 对应的具体计算操作~ return preState + data case DECREMENT: // 若果是减 return preState - data default: return preState } }
-
"UI组件" 使用参数
如下,UI组件可以通过props获得参数,而没有必要涉及“store",达到了代码分离的效果。
./componients/Count/index.jsx
import React, { Component } from 'react' export default class Count extends Component { state = {carName:'奔驰c63'} // 加法 increment = ()=>{ const {value} = this.selectNumber this.props.jia(value*1) } // 减法 decrement = ()=>{ const {value} = this.selectNumber this.props.jian(value*1) } // 奇数再加 incrementIfOdd = ()=>{ 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() { //console.log('UI组件接收到的props是',this.props); 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.incrementIfOdd}>当前求和为奇数再加</button> <button onClick={this.incrementAsync}>异步加</button> </div> ) } }
优化 Container Componient
写法优化
//使用connect()()创建并暴露一个Count的容器组件 export default connect( state => ({count:state}), //mapDispatchToProps的一般写法 /* dispatch => ({ jia:number => dispatch(createIncrementAction(number)), jian:number => dispatch(createDecrementAction(number)), jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)), }) */ //mapDispatchToProps的简写 { jia:createIncrementAction, jian:createDecrementAction, jiaAsync:createIncrementAsyncAction, } )(Count)
监听优化
入口index.js中的对store中状态变化的监控也可以省去,因为容器组件默认已有。
这里重点是 Provider。当需要写一排排的 stores时,有没有必要?
App.js
import React, { Component } from 'react' import Count from './containers/Count' export default class App extends Component { render() { return ( <div> <Count/> // 这里省去了 <Count store={store}/>,所以store 需要以另一种方式提供给Count </div> ) } }
在App的 ”被引用”的外层 通过 provider 加上 store即可。
import React from 'react' import ReactDOM from 'react-dom' import App from './App' import store from './redux/store' import {Provider} from 'react-redux' ReactDOM.render( <Provider store={store}> <App/> </Provider>, document.getElementById('root') )
文件层面优化
把 UI组件的内容 合并进 容器组件即可。
import React, { Component } from 'react' //引入action import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' //引入connect用于连接UI组件与redux import {connect} from 'react-redux' //定义UI组件 class Count extends Component { state = {carName:'奔驰c63'} //加法 increment = ()=>{ const {value} = this.selectNumber this.props.jia(value*1) } //减法 decrement = ()=>{ const {value} = this.selectNumber this.props.jian(value*1) } //奇数再加 incrementIfOdd = ()=>{ 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() { //console.log('UI组件接收到的props是',this.props); 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.incrementIfOdd}>当前求和为奇数再加</button> <button onClick={this.incrementAsync}>异步加</button> </div> ) } } //使用connect()()创建并暴露一个Count的容器组件 export defaultconnect( state => ({count:state}), //mapDispatchToProps的一般写法 /* dispatch => ({ jia:number => dispatch(createIncrementAction(number)), jian:number => dispatch(createDecrementAction(number)), jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)), }) */ //mapDispatchToProps的简写 { jia:createIncrementAction, jian:createDecrementAction, jiaAsync:createIncrementAsyncAction, } )(Count)