文章目录
1. 前言
刚从 Vue+ElementUI 转来 React+BlueUI 着实有些被 React 前端数据流转给秀了;前端请求后端服务,服务响应返回数据,前端拿着数据渲染到 UI,这么简单的流程,到了 React 就被 action、reducer、store、和 state、props 数据传递给带进了胡同里。
2. Redux 单向数据流转特性
严格的单向数据流是 Redux 架构的设计核⼼
Redux 应⽤中数据的⽣命周期遵循下⾯ 4 个步骤:
- 调用 store.dispatch(action)
- Redux store 调用传入的 reducer 函数
- 根 reducer 把多个子 reducer 输出合并为一个状态树 state 树
- Redux 保存了根 reducer 返回的完整 state 树
2.1 Store 存储唯一的 State
Store 就是⽤来维持应⽤所有的 state tree 的⼀个对象。改变 store 内 state 的唯⼀途径是对它 dispatch ⼀个 action。
Middleware 是包装 store 下 dispatch() 的⾼阶函数,返回⼀个新的 dispatch 函数;redux-thunk 或 redux-promise ⽀持异步 actions 的 middleware;
Store 保存了根 Reducer 返回的完整 State 树,所有订阅 store.subscribe(listener) 的监听器都将被调⽤;监听器⾥可以调⽤ store.getState() 获得当前 state。拿到新的 state 来重新渲染更新 UI。
eg: 优惠券 store.js 伪代码
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
const createStoreWithMdware = applyMiddleware(
thunkMiddleware
)(createStore);
export default createStoreWithMdware;
2.2 Action 触发改变 State
Action 是一个对象,表明要改变 state;可以在任何地⽅调⽤ store.dispatch(action) 来触发改变 state。
如下定义一个 action,获取后端服务优惠券列表的动作;一般 action 包含 type 和 data 属性,用来 type 标识不同的 action 对象,data 是待处理放入 state 树的数据。
eg: 优惠券活动 action.js 伪代码
export function getMarketingCouponList(params) {
return (dispatch) => {
ajax({
api: 'http://localhost:8080/coupon/findCouponActivityListByPage',
method: 'post',
data: params
}, (json) => {
dispatch({
type: 'GET_LIST_SUCCESS',
data: json.data
});
}, (json) => {
dispatch({
type: 'GET_LIST_FAILED',
data: json
});
});
};
}
2.3 Reducer 返回新的 State
Reducer 函数接受两个参数,当前 state 树和 action(包含待放入 state 的数据);
根 reducer 把多个子 reducer 输出合并为一个状态树 state 树
eg: 优惠券活动 reducer.js(marketingCoupon.js) 伪代码
const initialState = {
// 优惠券活动数据
activityData: [],
couponData: []
};
const defaultAction = {
type: 'doNothing'
};
// reducer 处理 (state, action)
export default function index(state = initialState, action = defaultAction) {
switch (action.type) {
case 'GET_LIST_SUCCESS':
return Object.assign({}, state, {
listLoading: false,
activityData: action.data
});
default:
return state;
}
}
eg:根rootReducer.js
import { routerReducer } from 'react-router-redux';
import { combineReducers } from 'redux';
import marketingCoupon from './marketingCoupon/';
// 将现有的reduces加上路由的reducer
const rootReducer = combineReducers({
marketingCoupon,
routing: routerReducer
});
export default rootReducer;
3. react-redux
3.1 安装 React-redux 库
npm install --save react-redux
3.2 搭配 React 使用
Redux 要支持 React 需要安装 react-redux 库;Redux 的 React 绑定库是基于 容器组件和展示组件相分离的。
你可以直接使⽤ store.subscribe() 来编写容器组件;建议使用 React Redux 库的 connect() ⽅法来⽣成,⽅法做了性能优化来避免很多不必要的重复渲染。
connect() ⽅法有两个主要的参数,⽽且都是可选的。第⼀个参数 mapStateToProps 是个函数,让你在数据变化时从 store 获取数据,并作为 props 传到组件中。第⼆个参数 mapDispatchToProps 依然是函数,让你可以 使⽤ store 的 dispatch ⽅法,通常都是创建 action 创建函数并预先绑定, 那么在调⽤时就能直接分发 action。
在执⾏ connect() 时没有指定 mapDispatchToProps ⽅法,React Redux 默认将 dispatch 作为 prop 传⼊。
3.3 “优惠券活动“伪代码
eg: 优惠券活动页面 couponActivity.jsx 伪代码
import React from 'react';
import { connect } from 'react-redux';
// 定义 action 对象的 action.js
import * as actions from '../../actions/marketingCoupon';
class MarketingCoupon extends React.Component {
constructor(props) {
super(props);
this.state={};
this.field = new Field(this);
}
// 进入页面加载
componentDidMount(){
window.onSearchMarketingCoupon=this.onSearch.bind(this);
this.onSearch(1);
}
pageChange=(value)=> {
this.onSearch(value);
};
//查询列表
onSearch(pageIndex){
const { dispatch } = this.props;
let params= {
page:pageIndex,
pageSize:10,
activityName:this.state.filterActivityName
};
dispatch(actions.getMarketingCouponList(params));
}
// 表格数据自定义渲染 end
render() {
return (
<div>
<div>
<div>
<Table dataSource={this.props.data.activityData.list} primaryKey="activityId" isLoading = {this.state.listLoading} hasBorder={false} >
<Table.Column title="活动名称" dataIndex="activityName"/>
<Table.Column title="创建时间" dataIndex="createTime" style={{textAlign: 'center', width: '150px'}}/>
</Table>
</div>
<div className="page">
<Pagination defaultCurrent={this.props.data.activityData.pageNumber} pageSize={this.props.data.activityData.pageSize} total={this.props.data.activityData.totalRow} onChange={this.pageChange.bind(this)}/>
</div>
</div>
</div>
);
}
}
export default connect((state) => {
return {
data:state.marketingCoupon
};
})(MarketingCoupon);
3.4 “优惠券活动“数据流转
(1)store 存储着唯一的 state 树;
(2)进入页面,触发 onSearch() 方法来查询优惠券列表,ajax 请求后端服务获取响应结果集;
(3)此处使用 connect() 生成容器组件,从 props 中拿到 dispatch,通过 dispatch 派发一个 action 对象(参考 action.js);
(4)reducer 函数处理当前 state tree 和 action.data 返回新的 state 树(参考 reducer.js);
(5)通过 connect() 获取新的 state 树,渲染数据,props 的数据更新相应的 UI(参考 couponActivity.jsx);
扩展:
容器组件和展示组件
简单来说,展示组件就是普通的 React 组件,比如 Button,Dialog。容器组件就是就是使⽤ store.subscribe() 从 Redux state 树中读取部分数据,并通过 props 来把这些数据提供给要渲染的组件。可以把展示组件和 Redux 关联起来。
展示组件 | 容器组件 | |
---|---|---|
说明 | 描述如何展现 | 描述如何运行 |
数据来源 | props | state |
数据修改 | 调用props中的回调函数 | store.dispatch(action) |
功能 | (1)读取 State;(2)派发 Action;(3)定义 mapDispatchToProps() 方法接收 dispatch() 方法,注入到 props 的回调方法,供展示组件使用 props.dispatch() |
上一篇:Redux 词汇,不巧你也不熟
Power By niaonao, The End