Why Redux and How
George Zou
将更多的业务逻辑放到前端实现
- 更好的交互体验
- 前后端分离
- 减轻服务器压力
- 同一套API可以应用于多终端
使得页面更加复杂
更多交互 => 更多的业务逻辑 => 更多的代码
难以迭代维护
依赖模糊
请求过多
Managing complexity is the most important technical topic in software development. In my view, it’s so important that Software’s Primary Technical Imperative has to be managing complexity.
软件开发的首要任务是控制复杂度
– Steve McConnell in Code Complete (more)
处理复杂前端架构的诀窍在于
提高可预测性
React 就是这么做的
render(){
return (<div>
<header>
<title>xxx's Order List</title>
</header>
<table>
<tr>
<td>Order Id</td>
<td>Details</td>
</tr>
</table>
</div>)
}
View
A Simple React Component
componentDidMount(){
$.post('app/init',{},function(data){
this.setState(data)
})
}
Data Manage
Require Data From API
Wait! What had happened?
OptionA => Request A => Result A
OptionB => Request B => Result B
...
Result A: this.setState({a: 1})
Result B: this.setState({a: 2})
...
Why state.a = 2 ?
用户进行了那些操作?
如果代码需要修改
Where can I find ajax request?
What do you do with state?
View and Model or State
从View中抽离数据模型
对数据(状态)进行统一的管理
数据 ==> 视图
动态渲染
What react do
render
可预测状态容器
What We Need
Predictable State Container
State?
状态树
Action
Reducer
View
(previousState, action) => newState
{
type: 'fetch-apple',
payload: {
apple: {
weight: '10斤',
}
}
}
我要吃一个十斤的大苹果
给你,不客气
Action Creator
Redux
{
type: 'tologin',
payload: {
username: username,
password: password
}
}
Action
一个普通的Object,type属性是必须的
描述当前所进行的操作操作。
关于标准Action的
“国际惯例”
制造action 的 pure fucntion
function LoginActionCreator(username, password) {
let status = false;
if(username === 'admin' && password === '123'){
status = true;
}
return {
type: 'tologin',
payload: {
status: status
}
}
}
Action Creator
Reducer
描述状态(state)如何改变
export default function usersReducer(state = initialState, action) {
switch(action.type) {
case 'login':
return {
...state,
status: aciton.payload.status
};
case 'change-contact-phonenum':
return state.map(contact =>
contact.id === action.id ?
{ ...contact, phoneNum: action.payload.phoneNum } :
contact
);
}
}
Reducer
描述状态(state)如何改变
import { combineReducers } from 'redux';
import user from './user';
import blacklist from './blacklist';
const rootReducer = combineReducers({
user:user,
blacklist: blacklist
});
连接多个模块的Reducer
组成完整的状态树
- 获取状态 getState()
- 更新状态 dispatch(action)
- 注册监听 subscribe(listener)
Store
Middleware
Action 到达 Reducer 时间点之前提供第三方补充
function validMiddleware(action) {
if(!action.payload.login) {
action.payload.text = "请登录";
}
}
function logMiddleware(action) {
log.info(action.type, action.payload);
}
自定义一些有意思的Middleware
return isPromise(action.payload)
? action.payload.then(
result => dispatch({ ...action, payload: result }),
error => {
dispatch({ ...action, payload: error, error: true });
return Promise.reject(error);
}
)
: next(action);
};
Redux-Promise
Action Creator 中进行异步操作
More Advice
Example
var UserList = React.createClass({
getInitialState: function() {
return {
users: []
}
},
componentDidMount: function() {
var _this = this;
$.get('/path/to/user-api').then(function(response) {
_this.setState({users: response})
});
},
render: function() {
return (
<ul className="user-list">
{this.state.users.map(function(user) {
return (
<li key={user.id}>
<Link to="{'/users/' + user.id}">{user.name}</Link>
</li>
);
})}
</ul>
);
}
});
It work well right?
But,如果我们还需要在开始的时候查询别的数据
componentDidMount: function() {
var _this = this;
$.get('/path/to/user-api').then(function(response) {
_this.setState({users: response})
$.get('/path/user-stats').then(function(response) {
//do something...
}
});
},
使得代码中数据操作和模板组件
强耦合
Separating Presentational and Container Components
分离容器组件和可视化组件
可视化组件
Presentational Components
import React from 'react';
class Teachers extends React.Component{
constructor(props){
super(props);
}
render(){
let { teacherList } = this.props;
return (
<div>
<h2>Teacher List</h2>
<div>
<table>
<thead>
<th>
<td>ID</td>
<td>Name</td>
<td>Phone</td>
<td>Address</td>
</th>
</thead>
<tbody>
{
teacherList.map(teacher => {
return (
<tr>
<td>{teacher.id}</td>
<td>{teacher.name}</td>
<td>{teacher.phone}</td>
<td>{teacher.address}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
</div>
)
}
}
export default Teachers;
容器组件组件
Container Components
class TeacherContainer extends React.Component{
constructor(props){
super(props);
}
getPageParam(){
let page = parseInt(this.props.params.pageNum);
return isNaN(pagfe) ? 1 : page;
}
getCourseCode(){
let courseCode = this.props.params.courseCode;
return courseCode;
}
componentWillMount(){
let courseCode = this.getCourseCode();
let page = this.getPageParam();
this.props.getTeachers({
courseCode,
page
})
}
componentWillReceiveProps(props){
let courseCodeNow = this.getCourseCode(),
courseCodeNext = props.params.courseCode;
let pageNow = this.getPageParam(),
pageNext = parseInt(props.params.pageNum);
if( pageNow != pageNext
|| courseCodeNow != courseCodeNext){
}
}
render(){
return (
<Teachers {...this.props} />
)
}
}
function mapStateToProps(store){
return {
teachers:store.teachers.teachers
}
}
export default connect(mapStateToProps,{ getTeachers })(TeacherContainer);
Why Redux
一切围绕“资料流”
Redux程序中全部的资料都遵循一样的生命周期模式,使得程序资料可预测
Predictable
Thanks & Question
参考资料
@GeorgeZouQ
Why Redux
By georgezou
Why Redux
- 1,574