Redux是受到了Facebook Flux和Elm啟發(fā)的應(yīng)用構(gòu)架篡石。Redux使用了類(lèi)似于Flux的單向數(shù)據(jù)流柔昼,但是它只有一個(gè)單一的store對(duì)象,這個(gè)store對(duì)象通過(guò)克隆原始的store來(lái)改變户矢,它調(diào)用reducer將action和之前的state作為參數(shù)冠蒋,reducer并不產(chǎn)生副作用。Redux中沒(méi)有Dispatcher欢搜。
Redux 和傳統(tǒng) Flux 框架的比較
網(wǎng)上有很多關(guān)于flux與redux的框架圖询微,我個(gè)人感覺(jué)這兩張最為直接易懂,所以就借用過(guò)來(lái)狂巢,在此對(duì)原圖的作者表示感謝撑毛。
不像Flux,在Redux中有一個(gè)單一的store對(duì)象唧领,包含整個(gè)應(yīng)用程序的state藻雌。這個(gè)store是由對(duì)象樹(shù)結(jié)構(gòu)組成的,它是不變的斩个。每次state需要改變的時(shí)候胯杭,一個(gè)新的對(duì)象樹(shù)就創(chuàng)造了出來(lái),合并了先前state中的數(shù)據(jù)和改變的數(shù)據(jù)受啥。當(dāng)一個(gè)action對(duì)象被分派到store中的時(shí)候做个,改變就被觸發(fā)。action是一個(gè)簡(jiǎn)單的對(duì)象滚局,其中包含了需要執(zhí)行的操作的類(lèi)型以及一些負(fù)載居暖。改變由reducers 來(lái)執(zhí)行,reducers 是沒(méi)有副作用的純函數(shù)藤肢,將先前的state和一個(gè)action作為參數(shù)太闺。它們會(huì)返回由應(yīng)用action產(chǎn)生的新的state。
Store不是一個(gè)類(lèi)嘁圈,而是一個(gè)伴隨著一些方法的對(duì)象省骂。通過(guò)在應(yīng)用程序的最初的state執(zhí)行root reducer可以創(chuàng)造出store。為了擴(kuò)展應(yīng)用程序最住,我們需要添加附加的reducers钞澳。每個(gè)reducer都維護(hù)一個(gè)state樹(shù)的一支。Redux提供了一個(gè)方法涨缚,可以將reducers合并成一個(gè)轧粟,當(dāng)store被創(chuàng)造出來(lái)的時(shí)候,它可以做一個(gè)簡(jiǎn)單的調(diào)用。
不像Flux一樣逃延,在Redux中沒(méi)有主要的Dispatcher览妖。當(dāng)一個(gè)action需要被執(zhí)行時(shí)冤狡,store的dispatch()方法被調(diào)用菩貌,將action當(dāng)作參數(shù)卓鹿。然后所有的監(jiān)聽(tīng)器被通知state已經(jīng)改變了,它們可以選擇去獲取新的state拄丰,然后相應(yīng)地呈現(xiàn)相關(guān)組成部分。
Redux的三原則
1. Single source of truth單一數(shù)據(jù)源俐末,數(shù)據(jù)流向也是單一方向料按。整個(gè)應(yīng)用的state,存儲(chǔ)在唯一一個(gè)javascript對(duì)象中卓箫,同時(shí)也只有一個(gè)store用于存儲(chǔ)這個(gè)對(duì)象.
2. State is read-only狀態(tài)是只讀的载矿。唯一能改變state的方法,就是觸發(fā)action操作烹卒。action是用來(lái)描述正在發(fā)生的事件的一個(gè)對(duì)象闷盔。
** 3. Changes are made with pure functions**在改變state tree時(shí),用到action旅急,同時(shí)也需要編寫(xiě)對(duì)應(yīng)的reducers才能完成state改變操作逢勾。
我們都知道MVC的設(shè)計(jì)模式,它的業(yè)務(wù)邏輯藐吮、數(shù)據(jù)溺拱、界面顯示分離的方法給我們帶來(lái)的好處不言而喻。如果用MVC模式去看待React和Redux的話(huà)谣辞,React承擔(dān)的就是MVC中的View的角色迫摔,而Redux框架給我的感覺(jué)是扮演MVC中的model和controller,它負(fù)責(zé)接收View的交互事件泥从,然后將處理完成后的結(jié)果返回給View攒菠,View根據(jù)結(jié)果重新刷新渲染。
這樣做的好處是開(kāi)發(fā)者只需要專(zhuān)心實(shí)現(xiàn)View歉闰,業(yè)務(wù)邏輯和數(shù)據(jù)從View中剝離出來(lái)辖众,使項(xiàng)目結(jié)構(gòu)分層清晰,代碼職責(zé)均衡和敬,降低視圖凹炸、數(shù)據(jù)、業(yè)務(wù)邏輯之間的耦合度昼弟。整個(gè)數(shù)據(jù)的流向是單一的啤它,使結(jié)果是可預(yù)測(cè)的。
ReactNative項(xiàng)目Redux框架的使用
安裝依賴(lài)包
. redux
. react-redux
. redux-thunk(一個(gè)異步的中間件實(shí)現(xiàn)庫(kù))
. redux-persist(redux-persist是Redux的持久化的實(shí)現(xiàn),可根據(jù)項(xiàng)目需求來(lái)確定要不要安裝)
為了我們的 app 能在沒(méi)有網(wǎng)絡(luò)或者網(wǎng)絡(luò)條件不好的情況下工作变骡,我們需要離線(xiàn)的本地存儲(chǔ)±牒眨現(xiàn)代的應(yīng)用包括 SPA(單頁(yè)面應(yīng)用, Single Page Application) ,原生 App 都對(duì)狀態(tài)持久化有強(qiáng)烈的需求塌碌,瀏覽器提供了 LocalStorage 渊胸、IndexedDB 等持久化方案和標(biāo)準(zhǔn),React Native 提供了 AsyncStorage 都是用來(lái)解決這些問(wèn)題台妆。
在項(xiàng)目的根目錄下使用npm install命令安裝依賴(lài)包:
$ npm install packagename --save
Redux模塊的編寫(xiě)
. Action
store數(shù)據(jù)的唯一來(lái)源翎猛,如果我們想修改store中的數(shù)據(jù),觸發(fā)Action是唯一方法接剩,它包含一個(gè)類(lèi)型以及相關(guān)數(shù)據(jù)切厘,通過(guò) Store 的 dispatch() 函數(shù)發(fā)送到 Store。
//types.js
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
//actions.js
import * as types from './types';
export function increment() {
return {
type: types.INCREMENT
};
}
export function decrement() {
return {
type: types.DECREMENT
};
}
. Reducer
reducer是一個(gè)純函數(shù),Action只是用來(lái)描述事情發(fā)生懊缺,具體的業(yè)務(wù)邏輯操作和state的更新是交給Reducer來(lái)處理疫稿,它接收一個(gè)之前的 state和一個(gè) Action;并基于此 Action 將會(huì)產(chǎn)生的影響鹃两,返回一個(gè)新的 state遗座。
。
//count.js
import * as types from '../actions/types';
const initialState = {
count: 0
};
export default function counter(state = initialState, action = {}) {
switch (action.type) {
case types.INCREMENT:
return {
...state,
count: state.count + 1
};
case types.DECREMENT:
return {
...state,
count: state.count - 1
};
default:
return state;
}
}
需要?jiǎng)?chuàng)建一個(gè)index.js用來(lái)導(dǎo)出reducers怔毛,否則也會(huì)報(bào)錯(cuò)找不到index.js:
import count from './count';
export {
count
};
Reducer可以不止一個(gè)员萍,我們?cè)谠O(shè)計(jì)的時(shí)候可以根據(jù)實(shí)際的業(yè)務(wù)邏輯來(lái)構(gòu)建若干個(gè)Reducer。但是最終傳遞給store的需要是一個(gè)Reducer拣度。這里Redux提供了combineReducers方法碎绎,把reducers組合成一個(gè)傳遞給store。
//合并多個(gè)reducer
const rootReducer = combineReducers({ reducer1, reducer2, reducer3});
. Store
Store 就是把 Reducer 和 action 聯(lián)系到一起的橋梁抗果。Store 接收 Action 的數(shù)據(jù)并將其連同當(dāng)前的 state樹(shù) (包含所有 state 的一種特殊的數(shù)據(jù)結(jié)構(gòu)筋帖,是一個(gè)單一的對(duì)象)發(fā)給 Reducer。
Store 有以下職責(zé):
- 維持應(yīng)用的 state冤馏;
- 提供 getState() 方法獲取 state日麸;
- 提供 dispatch(action) 方法更新 state;
- 接收新的state逮光,并替換當(dāng)前的state代箭;
- state變化時(shí),store觸發(fā)事件涕刚;
- 通過(guò) subscribe(listener) 注冊(cè)監(jiān)聽(tīng)器的組件從store提取新的state并更新組件嗡综。
Store本質(zhì)上是一個(gè)對(duì)象,它以樹(shù)的形式保存了整個(gè)應(yīng)用的State杜漠。并提供了一些方法极景。例如getState( ) 和 dispatch( )察净。Redux應(yīng)用只有惟一一個(gè)Store。Store通過(guò)createStore方法來(lái)創(chuàng)建盼樟,根據(jù)整個(gè)應(yīng)用的根Reducer的初始State氢卡。
//配置store
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import * as reducers from './reducers'
//添加中間件
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer);
React-Redux
Redux可以被任何的javascript框架應(yīng)用。但是它和React或者React Native配合得非常完美晨缴。原因就是React和React Native的組件都是基于state來(lái)渲染視圖的译秦。而Redux正是圍繞著state的管理而構(gòu)建起來(lái)的應(yīng)用框架。
React-Redux是React官方提供的庫(kù)喜庞。通過(guò)這個(gè)庫(kù)诀浪,我們可以很順暢的使用Redux架構(gòu)來(lái)構(gòu)建React或React Native應(yīng)用棋返。github地址延都。
React-Redux提供了兩個(gè)API:
render() {
return (
<Provider store = {store}>
<App />
</Provider>
);
}
//完整的root.js
'use strict';
import React, {Component} from 'react';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import * as reducers from './reducers'
import App from './app'
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer);
export default class Root extends Component {
render() {
return (
<Provider store = {store}>
<App />
</Provider>
);
}
}
//App.js
'use strict';
import React, { Component } from 'react';
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as actions from './actions/actions';
import FirstPage from './pages/firstPage';
import TwoPage from './pages/twoPage';
class App extends Component {
constructor(props) {
super(props);
}
render() {
const { state, actions } = this.props;
return (
<FirstPage
count={state.count}
{...actions}/>
);
}
}
//connect負(fù)責(zé)把React Component 和 Redux store 結(jié)合起來(lái)。通過(guò)connect睛竣,你就可以拿到store中的state晰房,并轉(zhuǎn)化成Component的props來(lái)使用了
export default connect(state => ({
//counter是reducer的文件名,否則會(huì)報(bào)返回不是一個(gè)對(duì)象的錯(cuò)誤
state: state.count
}),
(dispatch) => ({
//counterActions要包含組件觸發(fā)的action射沟,需要在改組件里導(dǎo)入相應(yīng)的action
actions: bindActionCreators(actions, dispatch)
})
)(App);
<Provider store>使應(yīng)用底層的component的connect( )方法能夠獲取到store。通常<Provider store>我們都用來(lái)包在整個(gè)應(yīng)用根Component的最外層验夯,這樣保證所有Component都能拿到store
猖吴。
Middleware
在redux里,middleware是發(fā)送action和action到達(dá)reducer之間的第三方擴(kuò)展挥转,也就是中間層海蔽。也可以這樣說(shuō),middleware是架在action和store之間的一座橋梁绑谣。如果不使用middleware的話(huà)党窜,Redux的store只支持同步數(shù)據(jù)流。也就是每當(dāng)我們dispatch action時(shí)借宵,state會(huì)被立即更新幌衣。同步只返回一個(gè)普通action對(duì)象。而異步操作中途會(huì)返回一個(gè)promise函數(shù)壤玫。當(dāng)然在promise函數(shù)處理完畢后也會(huì)返回一個(gè)普通action對(duì)象豁护。thunk中間件就是判斷如果返回的是函數(shù),則不傳導(dǎo)給reducer欲间,直到檢測(cè)到是普通action對(duì)象楚里,才交由reducer處理。
使用支持異步的middleware比如 redux-thunk或 redux-promise 能讓我們實(shí)現(xiàn)異步的數(shù)據(jù)流括改。你可以使用applyMiddleware( ) 來(lái)增強(qiáng) createStore( ) 腻豌。類(lèi)似這樣:
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer);
我們也可以編寫(xiě)自己的中間件家坎,來(lái)方便我們Debug,比如打印調(diào)用的action名稱(chēng)吝梅。
引入Redux框架前后組件代碼的對(duì)比:
引用前:
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
TouchableOpacity
} from 'react-native';
class TwoPage extends Component {
constructor(props) {
super(props);
this.state= {
count:0
};
}
increment() {
this.setState({
count:this.state.count+1
});
}
decrement() {
this.setState({
count:this.state.count-1
});
}
render() {
return (
<View style={styles.container}>
<Text style={styles.text}>{this.state.count}</Text>
<TouchableOpacity onPress={()=> this.increment()} style={styles.button}>
<Text>Up</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>this.decrement()} style={styles.button}>
<Text>Down</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
flex:1,
backgroundColor:'#4ec300',
justifyContent:'center',
alignItems:'center'
},
text:{
fontSize:50,
color:'#fff'
},
button: {
width: 100,
height: 30,
padding: 10,
backgroundColor: 'lightgray',
alignItems: 'center',
justifyContent: 'center',
margin: 3
}
});
export default TwoPage;
引用后:
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
TouchableOpacity
} from 'react-native';
class FirstPage extends Component {
render() {
const { count, increment, decrement } = this.props;
return (
<View style={styles.container}>
<Text style={styles.text}>{count}</Text>
<TouchableOpacity onPress={increment} style={styles.button}>
<Text>Up</Text>
</TouchableOpacity>
<TouchableOpacity onPress={decrement} style={styles.button}>
<Text>Down</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
flex:1,
backgroundColor:'#4ec300',
justifyContent:'center',
alignItems:'center'
},
text:{
fontSize:50,
color:'#fff'
},
button: {
width: 100,
height: 30,
padding: 10,
backgroundColor: 'lightgray',
alignItems: 'center',
justifyContent: 'center',
margin: 3
}
});
export default FirstPage;
通過(guò)對(duì)比引用Redux前后對(duì)比虱疏,大家再慢慢體會(huì)一下Redux框架吧!
由于我自己也是剛學(xué)習(xí)Redux框架苏携,所以還沒(méi)有在實(shí)際項(xiàng)目中引用做瞪,也是參考一些Demo和網(wǎng)上的教程邊參考學(xué)習(xí)邊體會(huì)代碼。在RN中使用Redux看起來(lái)很麻煩也很難理解右冻,只要跟著demo去邊敲代碼邊理解就能夠很容易掌握它装蓬。我之前也只是一直看博客什么的,但是不用代碼去簡(jiǎn)單的實(shí)現(xiàn)它纱扭,理解起來(lái)確實(shí)很困難牍帚,更別提去實(shí)際使用它了。根據(jù)Demo去做可以加深我們的理解乳蛾,畢竟“紙上得來(lái)終覺(jué)淺暗赶,絕知此事要躬行”嘛!
這里貼出GitHub上實(shí)現(xiàn)Redux框架的Demo:
https://github.com/ninty90/react-native-redux-demo
https://github.com/alinz/example-react-native-redux