第七章 redux
五、redux 异步编程
1. 理解
redux 默认是不能进行异步处理的, 某些时候应用中需要在 redux 中执行异步任务(ajax, 定时器)
2. 使用异步中间件
npm install --save redux-thunk
3. 代码 - 异步 action 版
3.1 store
import { createStore, applyMiddleware } from "redux" ;
import countReducer from "./count_reducer" ;
import thunk from 'redux-thunk'
export default createStore ( countReducer, applyMiddleware ( thunk) ) ;
3.2 count_action
import { INCREMENT , DECREMENT } from "./constant" ;
export const createIncrementAction = ( data ) => ( { type : INCREMENT , data } ) ;
export const createDecrementAction = ( data ) => ( { type : DECREMENT , data } ) ;
export const createIncrementAsyncAction = ( data, time ) => { return ( dispatch ) => { setTimeout ( ( ) => { dispatch ( createIncrementAction ( data) ) ; } , time) ; } ;
} ;
3.3 Count
import React, { Component } from "react" ;
import store from "../../redux/store" ;
import { createDecrementAction, createIncrementAction, createIncrementAsyncAction,
} from "../../redux/count_action" ; export default class Count extends Component { increment = ( ) => { const { value } = this . selectNumber; store. dispatch ( createIncrementAction ( value * 1 ) ) ; } ; decrement = ( ) => { const { value } = this . selectNumber; store. dispatch ( createDecrementAction ( value * 1 ) ) ; } ; incrementIfOdd = ( ) => { const { value } = this . selectNumber; const count = store. getState ( ) ; if ( count % 2 !== 0 ) { store. dispatch ( createIncrementAction ( value * 1 ) ) ; } } ; incrementAsync = ( ) => { const { value } = this . selectNumber; store. dispatch ( createIncrementAsyncAction ( value * 1 , 500 ) ) ; } ; render ( ) { return ( < div> < h1> 当前求和为:{ store. getState ( ) } < / 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> < / div> ) ; }
}
3.4 总结
( 1 ) .明确:延迟的动作不想交给组件自身,想交给action
( 2 ) .何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
( 3 ) .具体编码:1 ) .yarn add redux-thunk,并配置在store中2 ) .创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。3 ) .异步任务有结果后,分发一个同步的action去真正操作数据。
( 4 ) .备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。
六、react-redux
1. 理解
一个 react 插件库 专门用来简化 react 应用中使用 redux
2. react-Redux 将所有组件分成两大类
2.1 UI 组件
只负责 UI 的呈现,不带有任何业务逻辑 通过 props 接收数据(一般数据和函数) 不使用任何 Redux 的 API 一般保存在 components 文件夹下
2.2 容器组件
负责管理数据和业务逻辑,不负责 UI 的呈现 使用 Redux 的 API 一般保存在 containers 文件夹下
2.3 相关 API
2.3.1 Provider:让所有组件都可以得到 state 数据
< Provider store= { store} > < App / >
< / Provider>
2.3.2 connect:用于包装 UI 组件生成容器组件
import { connect } from 'react-redux' connect ( mapStateToprops, mapDispatchToProps) ( Counter)
2.3.3 mapStateToprops:将外部的数据(即 state 对象)转换为 UI 组件的标签属性
const mapStateToprops = function ( state ) { return { value : state}
}
2.3.4 mapDispatchToProps:将分发 action 的函数转换为 UI 组件的标签属性
3. 代码 - react-redux 的基本使用
安装:npm install react-redux
3.1 index
import React from "react" ;
import ReactDOM from "react-dom" ;
import App from "./App" ;
import store from "./redux/store" ; ReactDOM. render ( < App / > , document. getElementById ( "root" ) ) ;
store. subscribe ( ( ) => { ReactDOM. render ( < App / > , document. getElementById ( "root" ) ) ;
} ) ;
3.2 App
import React, { Component } from "react" ;
import Count from "./containers/Count" ;
import store from "./redux/store" ; export default class App extends Component { render ( ) { return ( < div> { } < Count store= { store} / > < / div> ) ; }
}
3.3 Count(Component)
import React, { Component } from "react" ; export default class Count extends Component { 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 ( ) { 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> < / div> ) ; }
}
3.4 Count(Container)
import CountUI from "../../components/Count" ;
import { createIncrementAction, createDecrementAction, createIncrementAsyncAction,
} from "../../redux/count_action" ;
import { connect } from "react-redux" ;
function mapStateToProps ( state ) { return { count : state } ;
}
function mapDispatchToProps ( dispatch ) { return { jia : ( number ) => dispatch ( createIncrementAction ( number) ) , jian : ( number ) => dispatch ( createDecrementAction ( number) ) , jiaAsync : ( number, time ) => dispatch ( createIncrementAsyncAction ( number, time) ) , } ;
}
export default connect ( mapStateToProps, mapDispatchToProps) ( CountUI) ;
3.5 总结
( 1 ) .明确两个概念:1 ) .UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。2 ) .容器组件:负责和redux通信,将结果交给UI组件。
( 2 ) .如何创建一个容器组件————靠 react-redux 的 connect函数connect( mapStateToProps,mapDispatchToProps) ( UI组件) -mapStateToProps:映射状态,返回值是一个对象-mapDispatchToProps:映射操作状态的方法,返回值是一个对象
( 3 ) .备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
( 4 ) .备注2:mapDispatchToProps,也可以是一个对象
4. 代码 - 优化 1:简写 mapDispatch
Count(Container)
import CountUI from "../../components/Count" ;
import { createIncrementAction, createDecrementAction, createIncrementAsyncAction,
} from "../../redux/count_action" ;
import { connect } from "react-redux" ;
export default connect ( ( state ) => ( { count : state } ) , { jia : createIncrementAction, jian : createDecrementAction, jiaAsync : createIncrementAsyncAction,
} ) ( CountUI) ;
5. 代码 - 优化 2:Provider 组件的使用
5.1 index
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" )
) ;
5.2 App
import React, { Component } from "react" ;
import Count from "./containers/Count" ; export default class App extends Component { render ( ) { return ( < div> < Count / > < / div> ) ; }
}
6. 代码 - 优化 3:整合 UI 组件和容器组件
6.1 Count(Container)
import React, { Component } from "react" ;
import { createIncrementAction, createDecrementAction, createIncrementAsyncAction,
} from "../../redux/count_action" ;
import { connect } from "react-redux" ;
class Count extends Component { 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 ( ) { 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> < / div> ) ; }
}
export default connect ( ( state ) => ( { count : state } ) , { jia : createIncrementAction, jian : createDecrementAction, jiaAsync : createIncrementAsyncAction,
} ) ( Count) ;
6.2 总结
( 1 ) .容器组件和UI组件整合一个文件
( 2 ) .无需自己给容器组件传递store,给< App/> 包裹一个< Provider store = { store} > 即可。
( 3 ) .使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。
( 4 ) .mapDispatchToProps也可以简单的写成一个对象
( 5 ) .一个组件要和redux“打交道”要经过哪几步?( 1 ) .定义好UI组件---不暴露( 2 ) .引入connect生成一个容器组件,并暴露,写法如下:connect( state = > ( { key:value} ) , //映射状态{ key:xxxxxAction} //映射操作状态的方法) ( UI组件) ( 3 ) .在UI组件中通过this.props.xxxxxxx读取和操作状态