上一篇我們學習了構建一個react項目以及配置webpack并運行項目,這篇我們在引入redux之前學習它的運行機制及設計思路。
Redux的架構思想
1. state和store 的概念
state是React中定義的應用狀態(tài),本質是一個數(shù)據(jù)集的普通對象,例如
/** 應用初始state **/
{
status: 0,
todos: []
}
store是應用狀態(tài)state的管理者,包含四個函數(shù):
getState() #獲取整個state
dispatch(action) #觸發(fā)state改變的唯一途徑
subscribe(listener) #可以理解為DOM中的addEventListener
replayceReducer(nextReducer) #一般在Webpack按需加載的時候用
二者的關系 state = store.getState()
Redux規(guī)定:一個應用只應有一個單一的store,其管理著唯一的應用狀態(tài)state且不能直接被修改,若要改變state,必須執(zhí)行dispatch一個action,這是修改應用狀態(tài)的唯一方法。
現(xiàn)在您只需要記住action只是一個包含type屬性的普通對象即可
例如{type:'INCREMENT'}
我們知道了state是通過store.getState()獲取的,那么store是怎么來的呢?那就需要用到了Redux定義的createStore方法了。
import { createStore } from 'redux'
...
//store是將reducer傳入生成的
const store = createStore(reducer, initialState)
現(xiàn)在您只需要記住reducer是一個函數(shù),負責更新并返回一個新的state,而initialState主要用于前后端同構的數(shù)據(jù)同步(詳情請關注React服務端渲染)
2. reducer
這里我們又要用到Redux中定義的另外一個方法combineReducers。
import { combineReducers } from 'redux'
...
const rootReducer = combineReducers({
captchaData,
loginState,
status,
userInfo
})
export default rootReducer
具體工作原理是dispatch執(zhí)行action之后,reducer負責根據(jù)action的type屬性重新將對應action的payload值賦給nextState
3.Action
上面我們提到了action實質的包含type屬性的普通對象,這個type是實現(xiàn)用戶行為的關鍵。例如我們增加一個支出分類
{
type: 'ADD_CATEGORY',
payload: {
id:1,
content:'交通'
}
}
當然,action可以根據(jù)具體的業(yè)務來自由設定,唯一的必要屬性就是type,甚至我們可以寫一個只包含type屬性的action
下面的action都是合法的
{
type: 'ADD_CATEGORY',
id:1,
content:'交通'
}
{
tyep: 'ADD_CATEGORY',
aabbcc: {
id: 1,
content: '交通'
}
}
{
type: 'ADD_CATEGORY'
}
雖然沒有約束,但最好還是遵循規(guī)范
這里有一個思考問題,action難道就只是包含type等一些屬性的對象嗎?這里留一個懸念,后面我在具體用Redux的過程中再講。
Redux優(yōu)缺點
Action Creator => action => store.dispatch(action) => reducer(state, action) =>
原statestate = nextState
優(yōu)點:清晰的數(shù)據(jù)流向,讓我們處理復雜的業(yè)務邏輯,可以更加方便的梳理業(yè)務線,action只負責執(zhí)行邏輯操作和數(shù)據(jù)獲取,reducer只負責返回數(shù)據(jù)集,然后connect給react中的組件使用,讓react只關心交互界面的邏輯,無需關心數(shù)據(jù)邏輯。
另外我們使用redux還有如下好處
- 方便地能夠將應用狀態(tài)存儲到本地并且重啟動時能夠讀取恢復狀態(tài)
- 方便地能夠在服務端完成初始狀態(tài)設置,并且完成狀態(tài)的服務端渲染
- 能夠序列化記錄用戶操作,能夠設置狀態(tài)快照,從而方便進行Bug報告與開發(fā)者的錯誤重現(xiàn)
- 能夠將用戶的操作或者事件傳遞給其他環(huán)境而不需要修改現(xiàn)有代碼
- 能夠添加重放或者撤銷功能而不需要重構代碼
- 能夠在開發(fā)過程中實現(xiàn)狀態(tài)歷史的回溯,或者根據(jù)Action的歷史重現(xiàn)狀態(tài)
- 能夠為開發(fā)者提供全面透徹的審視和修改現(xiàn)有開發(fā)工具的接口,從而保證產品的開發(fā)者能夠根據(jù)他們自己的應用需求打造專門的工具
- 能夠在復用現(xiàn)在大部分業(yè)務邏輯的基礎上構造不同的界面
使用Redux我們必須遵循Redux的守則
- 必須使用基本對象與數(shù)組來描述應用狀態(tài)
- 必須使用基本的對象來描述系統(tǒng)變化
- 必須使用純函數(shù)來處理系統(tǒng)中的業(yè)務邏輯
缺點:由于必須遵守Redux的守則,即使很簡單的業(yè)務邏輯,我們也必須有store,reducer,action.type。這樣給人的感覺就是殺雞用牛刀,所以對于簡單的業(yè)務邏輯,我們無需引用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()獲取,本質是一個存儲整個應用狀態(tài)數(shù)據(jù)集的對象
- reducer用于更新state并返回nextState的函數(shù),且reducer必須有返回值,否則nextState將為undefined
- action是一個包含type屬性的普通對象,用于dipatch來改變state
這一篇我們理解了Redux的幾個概念以及設計思想,下一篇我們要在項目中引用Redux庫并學習怎么使用它。