[React] 12 - Redux: async & middleware, container component

Ref: Redux 入门教程(二):中间件与异步操作

这里只是简单地了解中间件的概念,对于异步,貌似之后要讲的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))
./redux/store.js

 

//同步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)
    }
}
./redux/count_action.js

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>&nbsp; <button onClick={this.increment}>+</button>&nbsp; <button onClick={this.decrement}>-</button>&nbsp; <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp; <button onClick={this.incrementAsync}>异步加</button>&nbsp; </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>&nbsp;
                <button onClick={this.increment}>+</button>&nbsp;
                <button onClick={this.decrement}>-</button>&nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
                <button onClick={this.incrementAsync}>异步加</button>&nbsp;
            </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)

 

posted @ 2018-05-18 09:09  郝壹贰叁  阅读(420)  评论(0编辑  收藏  举报