Why Redux and How
George Zou
将更多的业务逻辑放到前端实现
更多交互 => 更多的业务逻辑 => 更多的代码
难以迭代维护
依赖模糊
请求过多
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
{
type: 'tologin',
payload: {
username: username,
password: password
}
}
一个普通的Object,type属性是必须的
描述当前所进行的操作操作。
制造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
描述状态(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
);
}
}
描述状态(state)如何改变
import { combineReducers } from 'redux';
import user from './user';
import blacklist from './blacklist';
const rootReducer = combineReducers({
user:user,
blacklist: blacklist
});
连接多个模块的Reducer
组成完整的状态树
Store
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 中进行异步操作
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>
);
}
});
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);
Redux程序中全部的资料都遵循一样的生命周期模式,使得程序资料可预测
参考资料
@GeorgeZouQ