深入淺出 Redux 的設計思想
lesson-2 主要內容: Redux 的設計思想
前言
Redux是什么呢?一個狀態管理工具。那是干嘛用的呢?都知道,React可以進行單頁應用的開發,可以對頁面 中各個模塊進行分割形成組件,而組件之間就避免不了事件的傳遞或數據的交互,那Redux就是用來對這些組件 的狀態進行管理的。就好比買家和賣家,快遞是交給第三方(Redux)去完成。
前言(續)
也行你會說,React不是有自己的一套數據傳遞和事件管理機制么,何必要引入第三方來插一腳,那么麻煩呢。 這里有一些場景來說明引用 React 是否必要:
1、你不需要使用Redux
用戶的使用方式非常簡單
用戶之間沒有協作
不需要與服務器大量交互,也沒有使用 WebSocket
視圖層(View)只從單一來源獲取數據
2、你需要Redux
用戶的使用方式復雜
不同身份的用戶有不同的使用方式(比如普通用戶和管理員)
多個用戶之間可以協作
與服務器大量交互,或者使用了WebSocket
View要從多個來源獲取數據
當然啦,本篇章講述的例子是不需要用到 Redux 這一套狀態管理工具的,但為了講解就需要簡單的例子來說明
用一個簡單的例子來深入淺出的理解 Redux的設計思想: 一個簡單的加減器,點擊加號加一,點擊減號減一
一、Redux 的工作流程 (快遞思想)
看圖感覺有點亂,梳理一下就清楚了,首先對以上圖幾個名詞做個解釋,它們分別是干嘛用的,有什么功能
1、 Store(快遞公司)
Store 可以看成是一個容器,整個應用只能有一個 Store ,就好比整個應用只能指定京東快遞公司來運貨。
import {createStore, combineReducers, applyMiddleware} from 'redux';
const store = createStore(reducer);
2、 State (快遞物件)
State :一狀態下的數據,一時間點下的數據。Store對象包含所有數據,想要得到某個時間點的數據,就要 對Store生成快照,得到一個數據集合,就叫做state。 store.getState() 可以得到state。 Redux規定一個State對應一個View,反之亦然。 就好比一個快遞物件只能給對應的主人,不能給其他人。
import {createStore, combineReducers, applyMiddleware} from 'redux';
const store = createStore(reducer);
store.getState()
3、Action (快遞單)
買家要買東西怎么辦,當然要先下單啦。用戶只能操作 View(比如對view進行點擊),用戶是接觸不到State的, 那State的變化對應View的變化,這就需要View通過一個Action對象來通知State的變化。就好比通過一個 快遞下單(發送一個Action),才有接下去的物流等一系列操作不是嗎。 Action是一個自定義對象,其中 type 屬性是必現的
cost action = {
type:'btnClick',
msg:'信息字符串,不是必現'
}
4、store.dispatch() (給快遞公司貨單)
store.dispatch() 是view發出Action的唯一方法,store.dispatch()接受一個Action對象,將他發出去。 現在還沒發貨,只是把訂單信息給京東快遞而已,京東是自營企業,有自己的物流倉庫和物流中心,搜到訂單信息 再根據訂單來發貨。
store.dispatch(action);
5、Reducer (包裝貨物)
Store 收到一個Action后,必須給出一個新的State,這樣view才會發生變化,而新的State的計算過程就是 Reducer來完成的。 就想收到一個訂單(Action)后,需要根據訂單來選取貨物,進行包裝。 Reducer是一個自定義函數,它接受Action和當前的State作為參賽,返回一個新的State
const reducer = (state=defaultState,action) =>{
switch (action.type) {
case 'btnClick':
return state + action.msg +'更新';
case '其他type':
return state + ‘其他action.msg’;
/*
可添加更多的 case type 來匹配不同的Action
*/
default:
return state;
}
}
接下來 我們把這套處理訂單的規則(Reducer)給快遞公司(Store),以后有訂單只需要根據這套規則來發貨就行了
import { createStore } from 'redux';
// store.dispatch 方法會觸發 Reducer 的自動執行
const store = createStore(reducer);
也許你會疑問那需要發送不同的 Action怎么辦 ,沒錯就是增加訂單規則就可以了,往 Reducer 里面增加 case ’type’的規則
6、store.subscribe() (接受快遞)
當 State一旦發生變化,那么 store.subscribe() 就會監聽到自動執行。 好比收到了快遞(秒送,哈哈) 那收到快遞能干嘛呢,每錯,就是在這里重新渲染 View 更新View咯。
let unsubscribe = store.subscribe(() =>
console.log(store.getState())
render(){
更新view !!!
}
);
// 也可以取消訂閱(監聽)
unsubscribe();
小結
下面講解 Redux 在 React下的應用 -- React-Redux
二、Redux 在 React下的應用 (React-Redux)
如果使用第一節講述的 Redux 的那一套狀態管理方法來對 React進行數據管理,也是可以的,但就有些麻煩。 比如咱們要在最后自己定義更新View
store.subscribe(() =>
console.log(store.getState())
render(){ // React 的 render()方法
更新view !!!
}
);
為了使用方便,Redux的作者封裝了一個React專用的庫 React-Redux
1、React-Redux
React-Redux 將組件分為兩大類,UI組件和容器組件。UI組件只負責UI的呈現,而容器組件用來管理數據和事件。 那怎么把交互和UI聯系起來呢,那就使用 React-Redux 提供的 connect 方法。
2、connect()
React-Redux 提供的 connect 方法。就是將 UI組件個容器組件聯系起來,那規則是什么呢? 需要有:
輸入邏輯:外部數據(state對象)轉為 UI組件的參數
輸出邏輯:用戶發出的動車 (Action對象)從UI組件傳遞出去
因此,connect方法的API是這樣的:
import { connect } from 'react-redux'
const Comp = connect(
mapStateToProps,
mapDispatchToProps
)(UI)</code></pre>
connect方法接受兩個參數:mapStateToProps 和 mapDispatchToProps ,前者負責輸入邏輯,將state映射到 UI組件的參數 props ,后者負責輸出邏輯,將用戶對UI的操作映射成Action。
(1)、mapStateToProps()
mapStateToProps是一個函數, 建立一個 state對象到UI組件的props對象的映射關系! 是映射到UI組件的props 對象上(關鍵點,多提醒一下,下面會做解釋)。
const mapStateToProps = (state) => {
return {
count: state.count
}
};
mapStateToProps 接受參數state,state的字段(state.count)賦值給 count 屬性,而count屬性是UI組件的 同名參數。 mapStateToProps會定義 Store,每當 state更新就會自動執行這個方法,那自動執行這個方法怎么就會更新UI呢, UI更新一個來自自身的 this.state 變化,還有一個來自 this.props 變化都會觸發React組件重新render(), 而 就上面例子 mapStateToProps 接受 state 的變化從而返回一個 賦值 count屬性,而這個屬性是對應UI組件的 props對象的映射,props變化自然會帶動UI組件的更新。
(2)、mapDispatchToProps()
mapDispatchToProps 是 connect的第二個參數,也是一個函數(還可以是一個對象)。mapDispatchToProps作為函數, 應該返回一個對象,該對象的每個鍵值對都是一個映射,定義了 UI 組件的參數怎樣發出 Action。又是一個映射到UI組件的 props參數上!!!
const mapDispatchToProps = (dispatch, ownProps) => {
return {
increase: (...args) => dispatch(actions.increase(...args)),
decrease: (...args) => dispatch(actions.decrease(...args))
}
};
上面 mapDispatchToProps函數接受兩個參數,返回一個對象,注意返回對象的屬性是對應UI組件的props參數的。 也就是UI組件怎么派發一個Action呢,那就是 UI組件調用 props.increase 就會執行 dispatch(Action) 操作,從而派發一個Action。
3、 組件
上面多次提到 屬性映射到props組件上,子組件在通過props拿到數據或方法進行操作,那props對應的組件是誰呢, 也就是子組件的父組件是誰呢。 React-Redux 就提供了 組件 來充當所有UI組件的容器組件,所有UI組件都可以利用props屬性想 組件拿數據。而 組件的數據由 store提供。
render(
<Provider store={store}>
<Index></Index>
</Provider>,
document.body.appendChild(document.createElement('div'))
);
小結
由下圖可以看出 React-Redux 的大致工作原理,可以看出UI組件確實只負責UI部分,只通過props參數拿數據和對外 派發數據,而沒有多做業務上的邏輯。而業務邏輯和數據呈現交給了容器組件,UI組件和容器組件是通過 connect() 方法鏈接的,內部是通過 mapStateToProps 和mapDispatchToProps進行數據傳遞的。整個應用的數據都會經過 Store這個中央處理器的處理。所以不同UI組件之間的數據交互可以把數據都丟給Store這個中央處理器處理,Store 再把處理好的數據回傳給各個UI組件就可以了 
三、小例子:加減器

用一段代碼來鞏固前面所學習的知識。實踐強化嘛!哈哈
Main.jsx:
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
/**
- 定義一個 Main組件
*/
class Main extends Component{
constructor(){
super();
this.pClick =() =>{
console.log('sssssssss');
};
this.state = {
num:0,
age:666
}
}
render(){
// 拿到 this.props 參數
const {count, increase, decrease} = this.props;
return(
<div>
<p className="lesson-2">React lesson-2</p>
<p>
---------------------------------
</p>
<div className="count">
<div>計數:{this.props.count}次</div>
<span className="btn" onClick={increase}>+</span>
<span className="btn" onClick={decrease}>-</span>
</div>
</div>
)
}
}
/**
- 用來給 組件傳遞數據
- @param state
*/
const mapStateToProps = (state) => {
return {
count: state.count
}
};
/*用來組件給 容器組件派發數據
- @param dispatch 派發 Action
- @param ownProps 組件自身的 props參數
*/
const mapDispatchToProps = (dispatch, ownProps) => {
return {
increase: (...args) => dispatch(actions.increase(...args)),
decrease: (...args) => dispatch(actions.decrease(...args))
}
};
/**
- actions
*/
const actions ={
increase:() => {
return {type: 'INCREASE'}
},
decrease: () => {
return {type: 'DECREASE'}
}
};
/**
- 連接 UI組件 和 容器組件
- @param mapStateToProps 輸入邏輯
- @param mapDispatchToProps 輸出邏輯
*/
const Comp = connect(mapStateToProps, mapDispatchToProps)(Main);
/**
- 輸出一個容器組件
*/
export default Comp;</code></pre>
App.js
import React,{Component,PropTypes} from 'react';
import ReactDOM, {render} from 'react-dom';
import {Provider,connect} from 'react-redux';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import Index from './Component/Main.jsx';
import './Style/comm.scss'
const store = createStore(reducer);
//監聽state變化
store.subscribe(() => {
//console.log(store.getState())
});
const reducer = (state = {count: 0}, action) => {
switch (action.type){
case 'INCREASE': return {count: state.count + 1};
case 'DECREASE': return {count: state.count - 1};
default: return state;
}
}
render(
<Provider store={store}>
<Index></Index>
</Provider>,
document.body.appendChild(document.createElement('div'))
);</code></pre>
你也可以 clone本項目運行調試
clone git@github.com:ZengTianShengZ/react-lesson.git
cd lesson-2
npm install
npm run hot
總結
以上是對 Redux 的學習和體會,很多資料來源于網上和大神的博客,如有疑問的話可以 issue, 覺得有幫助的話可以 star 一下本項目哦