前言
想要在react里面使用状态管理,redux就是最好的选择。更加详细的介绍:http://cn.redux.js.org/docs/basics/Reducers.html
Action
Action 本质上是 JavaScript 普通对象。我们约定,action 内必须使用一个字符串类型的 type
字段来表示将要执行的动作。多数情况下,type
会被定义成字符串常量。当应用规模越来越大时,建议使用单独的模块或文件来存放 action。
下面return的对象就是action,而return该对象的函数我们叫Action创建函数
// action
export const INCREMENT = "counter/INCREMENT"
export const DECREMENT ="counter/DECREMENT"
export const RESET ="counter/RESET"
export function increment(){
return {
type:INCREMENT
}
}
export function decrement() {
return {
type:DECREMENT
}
}
export function reset(){
return {
type:RESET
}
}
当然,action还可以返回一个函数,但是这里我们需要引入中间件react-thunk,在下面的异步请求action会介绍。
Reducer
reducer,就是通过对应不同的action.type,做不同的处理,return不同的state,改变state。例子:
//这是对应action里面的counter的reducers
import { INCREMENT, DECREMENT, RESET } from '../actions/counter';
/*
*初始化state
*/
const iniState={
count:0
};
/*
*reducer
*/
export default function reducer(state=iniState,action) {
switch (action.type) {
case INCREMENT:
console.log('count',state.count)
return {
count:state.count+1
};
case DECREMENT:
return{
count: state.count - 1
};
case RESET:
return{
count:0
};
default:
return state
}
}
这里也可以定义多个reducer,不过要借助combineReducers把多个reducer联合起来,再export出去。如下:
import { BEFORESEND, SUCCESS, FAIL} from '../actions/asyn'
//实现异步网络请求的reducer
import {combineReducers} from 'redux'
const initState={
status:'',
result:''
}
function beforeSend(state=initState,action){
switch (action.type) {
case BEFORESEND:
console.log('请求发起');
return Object.assign({},state,{
status:'状态-请求发起前'
})
break;
default:
return state
break;
}
}
function fetchResult(state=initState,action){
switch (action.type) {
case SUCCESS:
console.log('请求成功');
return Object.assign({},state,{
status:'状态-请求成功',
result:action.result
})
break;
case FAIL:
console.log('请求失败');
return Object.assign({},state,{
status:'状态-请求失败',
result:action.result
})
break
default:
return state;
break;
}
}
const asyn=combineReducers({
beforeSend,
fetchResult
})
export default asyn;
Store
在前面的章节中,我们学会了使用 action 来描述“发生了什么”,和使用 reducers 来根据 action 更新 state 的用法。
Store 就是把它们联系到一起的对象。Store 有以下职责:
- 维持应用的 state;
- 提供
getState()
方法获取 state; - 提供
dispatch(action)
方法更新 state; - 通过
subscribe(listener)
注册监听器; - 通过
subscribe(listener)
返回的函数注销监听器。
再次强调一下 Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合而不是创建多个 store。
根据已有的 reducer 来创建 store 是非常容易的。在前面,我们使用 combineReducers()
将多个 reducer 合并成为一个。
现在,我们在store中也要这样使用,我们将所有reducer导入,再把导入的所有reducer用combineReducers()合
并,传递给 createStore()
。
/**
* store是用于连接action和reducers,store的职责如下:
* 1、维持应用的 state;
* 2、提供 getState() 方法获取 state;
* 3、提供 dispatch(action) 触发reducers方法更新 state;
* 4、通过subscribe(listener) 注册监听器;
* 5、通过 subscribe(listener) 返回的函数注销监听器。
*/
import thunkMiddleware from 'redux-thunk'; //引入中间件,让action不仅返回一个对象,还可以返回函数,可以dispatch函数,用作请求异步。
import { createLogger } from 'redux-logger';
import { createStore, applyMiddleware} from 'redux';
import combineReducers from './reducers.js';
const loggerMiddleware = createLogger()
let store = createStore(
combineReducers
);
export default store;
此时,我们使用dispatch就可以发起action:
import {increment,decrement,reset} from './actions/counter';
import {add,des} from './actions/userInfo'
import store from './store';
//打印初始状态
console.log(store.getState());
/**
* 每次state更新时,打印日志
* subscribe() 返回一个函数用来注销监听器,执行unsubscribe(),就是注销监听器
*/
let unsubscribe=store.subscribe(()=>
console.log(store.getState())
)
store.dispatch(add());
store.dispatch(des());
unsubscribe();
此时,我们在控制台就能看到每次dispatch改变state的结果。
React和Redux搭配
那么,如果想在react组件里面使用redux,该如何使用呢?当然,我们可以import store到组件了,直接使用store.dispatch,但是,我们一般不会这样做。我们一般会使用Provider和connect。
Provider
<Provider store>
<Provider store>
使组件层级中的 connect()
方法都能够获得 Redux store。正常情况下,你的根组件应该嵌套在 <Provider>
中才能使用 connect()
方法。
provider可以把store传递给所有使用了connect的子组件。
普通例子:
ReactDOM.render(
<Provider store={store}>
<MyRootComponent />
</Provider>,
rootEl
)
在路由中使用:
const getRouter = () => (
<Provider store = {store} >
<Router>
<div>
<ul>
<li>
<Link to="/">首页</Link>
</li>
<li>
<Link to="/page1">Page1</Link>
</li>
</ul>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/page1" component={Page1}/>
</Switch>
</div>
</Router>
</ Provider>
);
connect
connect,用于组件和redux连接起来。其实就是相当于高阶组件,通过connet,把现有的组件,添加自己的逻辑,使得组件可以通过props使用redux的state和dispatch方法。
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
参数
[
mapStateToProps(state, [ownProps]): stateProps
] (Function): 如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps
函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。如果你省略了这个参数,你的组件将不会监听 Redux store。如果指定了该回调函数中的第二个参数ownProps
,则该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapStateToProps
也会被调用(例如,当 props 接收到来自父组件一个小小的改动,那么你所使用的 ownProps 参数,mapStateToProps 都会被重新计算)。注意:在高级章节中,你需要更好地去控制渲染的性能,所用到的
mapStateToProps()
会返回一个函数。在这种情况下,那个函数将被作为mapStateToProps()
在独有的组件实例中调用。这样就允许你在每一个实例中去记录。你可以参考 #279 去测试和了解其中的详细内容。但在绝大多数的应用中不会用到。[
mapDispatchToProps(dispatch, [ownProps]): dispatchProps
] (Object or Function): 如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中dispatch
方法会将action creator的返回值作为参数执行。这些属性会被合并到组件的 props 中。
[mergeProps(stateProps, dispatchProps, ownProps): props
] (Function): 如果指定了这个参数, mapStateToProps()
与 mapDispatchToProps()
的执行结果和组件自身的 props
将传入到这个回调函数中。该回调函数返回的对象将作为 props 传递到被包装的组件中。你也许可以用这个回调函数,根据组件的 props 来筛选部分的 state 数据,或者把 props 中的某个特定变量与 action creator 绑定在一起。如果你省略这个参数,默认情况下返回 Object.assign({}, ownProps, stateProps, dispatchProps)
的结果。
[options
] (Object) 如果指定这个参数,可以定制 connector 的行为。
- [
pure = true
] (Boolean): 如果为 true,connector 将执行shouldComponentUpdate
并且浅对比mergeProps
的结果,避免不必要的更新,前提是当前组件是一个“纯”组件,它不依赖于任何的输入或 state 而只依赖于 props 和 Redux store 的 state。默认值为true
。 - [
withRef = false
] (Boolean): 如果为 true,connector 会保存一个对被被包含的组件实例的引用,该引用通过getWrappedInstance()
方法获得。默认值为false
。
简单的使用实例:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {increment} from '../../redux/actions/counter'
const mapStateToProps=(state)=>{
return {
counter:state.counter.count
}
}
const mapDispatchToProps=(dispatch,ownProps) =>{
return {
increment:()=>dispatch(increment())
}
}
class Home extends Component {
constructor(props){
super(props);
this.state={
count:0
}
}
render() {
return (
<div>
this is home.<br/>
当前计数:{this.props.counter}<br/>
<button onClick={()=>this.props.increment()}>自增</button>
</div>
)
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Home)
异步Action的使用
如果,我们需要发起一个网络请求,结果需要改变redux里面的状态。我们可以在请求前调用dispatch,请求结束再dispatch另外一个action。这里,我们可以通过中间件的使用,只需要使用一个dispatch,即可完成请求前后的dispatch。
react-thunk
使用react-thunk中间件,可以帮助我们把action创建函数只能返回对象,改造成可以返回一个function。
store.js
import thunkMiddleware from 'redux-thunk'; //引入中间件,让action不仅返回一个对象,还可以返回函数,可以dispatch函数,用作请求异步。
import { createLogger } from 'redux-logger';
import { createStore, applyMiddleware} from 'redux';
import combineReducers from './reducers.js';
const loggerMiddleware = createLogger()
let store = createStore(
combineReducers,
applyMiddleware(
thunkMiddleware, // 允许我们 dispatch() 函数
loggerMiddleware // 一个很便捷的 middleware,用来打印 action 日志
)
);
export default store;
reducer.js
export const BEFORESEND='asyn/beforeSend';
export const SUCCESS = 'asyn/success';
export const FAIL = 'asyn/fail';
export function beforeSend() {
return{
type: BEFORESEND
}
}
export function success(result) {
return{
type: SUCCESS,
result
}
}
export function fail(result) {
return {
type: FAIL,
result
}
}
//thunk action函数
//用法和普通action一样,只是return不是一个对象,是函数
export function fetchPosts() {
//使用了thunk中间件,知道该如何 处理此函数
/**
* 把dispatch方法通过参数形式传递给函数
* 该函数内部就能自己dispatch action
*/
return function (dispatch) {
dispatch(beforeSend()); //这里dispatch一个方法,代表请求发起前
/**
* 这里使用Promise+settimeout模拟网络请求
*/
new Promise((resolve,reject)=>{
setTimeout(() => {
reject('结果-异步请求失败')
resolve('结果-异步请求成功');
}, 300);
}).then((data)=>{
/**
* 使用请求结果来更新应用对应的state,
* 可以使用多个dispatch
*/
dispatch(success(data));
}).catch((data)=>{
dispatch(fail(data));
})
}
}
直接使用dispatch,即可以发起一个异步action
import {fetchPosts} from '../../redux/actions/asyn';
const mapDispatchToProps=(dispatch,ownProps) =>{
return {
fetch:()=>dispatch(fetchPosts())
}
}
参考:http://cn.redux.js.org/docs/react-redux/api.html