上一篇我们学习了构建一个react项目以及配置webpack并运行项目,这篇我们在引入redux之前学习它的运行机制及设计思路。
Redux的架构思想
1. state和store 的概念
state是React中定义的应用状态,本质是一个数据集的普通对象,例如
/** 应用初始state **/
{
status: 0,
todos: []
}
store是应用状态state的管理者,包含四个函数:
getState() #获取整个state
dispatch(action) #触发state改变的唯一途径
subscribe(listener) #可以理解为DOM中的addEventListener
replayceReducer(nextReducer) #一般在Webpack按需加载的时候用
二者的关系 state = store.getState()
Redux规定:一个应用只应有一个单一的store,其管理着唯一的应用状态state且不能直接被修改,若要改变state,必须执行dispatch一个action,这是修改应用状态的唯一方法。
现在您只需要记住action只是一个包含type属性的普通对象即可
例如{type:'INCREMENT'}
我们知道了state是通过store.getState()获取的,那么store是怎么来的呢?那就需要用到了Redux定义的createStore方法了。
import { createStore } from 'redux'
...
//store是将reducer传入生成的
const store = createStore(reducer, initialState)
现在您只需要记住reducer是一个函数,负责更新并返回一个新的state,而initialState主要用于前后端同构的数据同步(详情请关注React服务端渲染)
2. reducer
这里我们又要用到Redux中定义的另外一个方法combineReducers。
import { combineReducers } from 'redux'
...
const rootReducer = combineReducers({
captchaData,
loginState,
status,
userInfo
})
export default rootReducer
具体工作原理是dispatch执行action之后,reducer负责根据action的type属性重新将对应action的payload值赋给nextState
3.Action
上面我们提到了action实质的包含type属性的普通对象,这个type是实现用户行为的关键。例如我们增加一个支出分类
{
type: 'ADD_CATEGORY',
payload: {
id:1,
content:'交通'
}
}
当然,action可以根据具体的业务来自由设定,唯一的必要属性就是type,甚至我们可以写一个只包含type属性的action
下面的action都是合法的
{
type: 'ADD_CATEGORY',
id:1,
content:'交通'
}
{
tyep: 'ADD_CATEGORY',
aabbcc: {
id: 1,
content: '交通'
}
}
{
type: 'ADD_CATEGORY'
}
虽然没有约束,但最好还是遵循规范
这里有一个思考问题,action难道就只是包含type等一些属性的对象吗?这里留一个悬念,后面我在具体用Redux的过程中再讲。
Redux优缺点
Action Creator => action => store.dispatch(action) => reducer(state, action) =>
原statestate = nextState
优点:清晰的数据流向,让我们处理复杂的业务逻辑,可以更加方便的梳理业务线,action只负责执行逻辑操作和数据获取,reducer只负责返回数据集,然后connect给react中的组件使用,让react只关心交互界面的逻辑,无需关心数据逻辑。
另外我们使用redux还有如下好处
- 方便地能够将应用状态存储到本地并且重启动时能够读取恢复状态
- 方便地能够在服务端完成初始状态设置,并且完成状态的服务端渲染
- 能够序列化记录用户操作,能够设置状态快照,从而方便进行Bug报告与开发者的错误重现
- 能够将用户的操作或者事件传递给其他环境而不需要修改现有代码
- 能够添加重放或者撤销功能而不需要重构代码
- 能够在开发过程中实现状态历史的回溯,或者根据Action的历史重现状态
- 能够为开发者提供全面透彻的审视和修改现有开发工具的接口,从而保证产品的开发者能够根据他们自己的应用需求打造专门的工具
- 能够在复用现在大部分业务逻辑的基础上构造不同的界面
使用Redux我们必须遵循Redux的守则
- 必须使用基本对象与数组来描述应用状态
- 必须使用基本的对象来描述系统变化
- 必须使用纯函数来处理系统中的业务逻辑
缺点:由于必须遵守Redux的守则,即使很简单的业务逻辑,我们也必须有store,reducer,action.type。这样给人的感觉就是杀鸡用牛刀,所以对于简单的业务逻辑,我们无需引用Redux库。
最后,Redux真正的灵魂在于其设计思想,很多时候并不一定需要引用Redux库本身,但可以尝试使用redux的思想。
import React, { Component } from 'react'
class Counter extends Component {
state = { value: 0 }
increment = () => {
this.setState(prevState => ({
value: prevState.value + 1
}))
}
decrement = () => {
this.setState(prevState => ({
value: prevState.value - 1
}))
}
render() {
return (
<div>
{this.state.value}
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
)
}
使用redux,我们可能会有禁用Local State的习惯,其实Local State有时候恰恰是用最简单的方法帮我们解决问题。我们对上面代码用redux思想进行改造
import React, { Component } from 'react'
const counter = (state = { value:0 }, action) => {
switch (action.type) {
case 'INCREMENT':
return { value: state.value + 1}
case 'DECREMENT':
return { value: state.value - 1}
default:
return state
}
}
class Counter extends Component {
state = counter(undefined, {})
dispatch(action) {
this.setState(prevState => counter(prevState, action))
}
increment = () => {
this.dispatch({type: 'INCREMENT'})
}
decrement = () => {
this.dispatch({type: 'DECREMENT'})
}
render() {
return (
<div>
{this.state.value}
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
)
}
}
总结
- store由Redux的 createStore(reducer) 生成
- state通过store.getState()获取,本质是一个存储整个应用状态数据集的对象
- reducer用于更新state并返回nextState的函数,且reducer必须有返回值,否则nextState将为undefined
- action是一个包含type属性的普通对象,用于dipatch来改变state
这一篇我们理解了Redux的几个概念以及设计思想,下一篇我们要在项目中引用Redux库并学习怎么使用它。