Redux是受到了Facebook Flux和Elm啟發(fā)的應用構(gòu)架。Redux使用了類似于Flux的單向數(shù)據(jù)流,但是它只有一個單一的store對象,這個store對象通過克隆原始的store來改變,它調(diào)用reducer將action和之前的state作為參數(shù),reducer并不產(chǎn)生副作用。Redux中沒有Dispatcher。
Redux 和傳統(tǒng) Flux 框架的比較
網(wǎng)上有很多關(guān)于flux與redux的框架圖,我個人感覺這兩張最為直接易懂,所以就借用過來,在此對原圖的作者表示感謝。
不像Flux,在Redux中有一個單一的store對象,包含整個應用程序的state。這個store是由對象樹結(jié)構(gòu)組成的,它是不變的。每次state需要改變的時候,一個新的對象樹就創(chuàng)造了出來,合并了先前state中的數(shù)據(jù)和改變的數(shù)據(jù)。當一個action對象被分派到store中的時候,改變就被觸發(fā)。action是一個簡單的對象,其中包含了需要執(zhí)行的操作的類型以及一些負載。改變由reducers 來執(zhí)行,reducers 是沒有副作用的純函數(shù),將先前的state和一個action作為參數(shù)。它們會返回由應用action產(chǎn)生的新的state。
Store不是一個類,而是一個伴隨著一些方法的對象。通過在應用程序的最初的state執(zhí)行root reducer可以創(chuàng)造出store。為了擴展應用程序,我們需要添加附加的reducers。每個reducer都維護一個state樹的一支。Redux提供了一個方法,可以將reducers合并成一個,當store被創(chuàng)造出來的時候,它可以做一個簡單的調(diào)用。
不像Flux一樣,在Redux中沒有主要的Dispatcher。當一個action需要被執(zhí)行時,store的dispatch()方法被調(diào)用,將action當作參數(shù)。然后所有的監(jiān)聽器被通知state已經(jīng)改變了,它們可以選擇去獲取新的state,然后相應地呈現(xiàn)相關(guān)組成部分。
Redux的三原則
1. Single source of truth單一數(shù)據(jù)源,數(shù)據(jù)流向也是單一方向。整個應用的state,存儲在唯一一個javascript對象中,同時也只有一個store用于存儲這個對象.
2. State is read-only狀態(tài)是只讀的。唯一能改變state的方法,就是觸發(fā)action操作。action是用來描述正在發(fā)生的事件的一個對象。
** 3. Changes are made with pure functions**在改變state tree時,用到action,同時也需要編寫對應的reducers才能完成state改變操作。
我們都知道MVC的設計模式,它的業(yè)務邏輯、數(shù)據(jù)、界面顯示分離的方法給我們帶來的好處不言而喻。如果用MVC模式去看待React和Redux的話,React承擔的就是MVC中的View的角色,而Redux框架給我的感覺是扮演MVC中的model和controller,它負責接收View的交互事件,然后將處理完成后的結(jié)果返回給View,View根據(jù)結(jié)果重新刷新渲染。
這樣做的好處是開發(fā)者只需要專心實現(xiàn)View,業(yè)務邏輯和數(shù)據(jù)從View中剝離出來,使項目結(jié)構(gòu)分層清晰,代碼職責均衡,降低視圖、數(shù)據(jù)、業(yè)務邏輯之間的耦合度。整個數(shù)據(jù)的流向是單一的,使結(jié)果是可預測的。
ReactNative項目Redux框架的使用
安裝依賴包
. redux
. react-redux
. redux-thunk(一個異步的中間件實現(xiàn)庫)
. redux-persist(redux-persist是Redux的持久化的實現(xiàn),可根據(jù)項目需求來確定要不要安裝)
為了我們的 app 能在沒有網(wǎng)絡或者網(wǎng)絡條件不好的情況下工作,我們需要離線的本地存儲?,F(xiàn)代的應用包括 SPA(單頁面應用, Single Page Application) ,原生 App 都對狀態(tài)持久化有強烈的需求,瀏覽器提供了 LocalStorage 、IndexedDB 等持久化方案和標準,React Native 提供了 AsyncStorage 都是用來解決這些問題。
在項目的根目錄下使用npm install命令安裝依賴包:
$ npm install packagename --save
Redux模塊的編寫
. Action
store數(shù)據(jù)的唯一來源,如果我們想修改store中的數(shù)據(jù),觸發(fā)Action是唯一方法,它包含一個類型以及相關(guān)數(shù)據(jù),通過 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是一個純函數(shù),Action只是用來描述事情發(fā)生,具體的業(yè)務邏輯操作和state的更新是交給Reducer來處理,它接收一個之前的 state和一個 Action;并基于此 Action 將會產(chǎn)生的影響,返回一個新的 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;
}
}
需要創(chuàng)建一個index.js用來導出reducers,否則也會報錯找不到index.js:
import count from './count';
export {
count
};
Reducer可以不止一個,我們在設計的時候可以根據(jù)實際的業(yè)務邏輯來構(gòu)建若干個Reducer。但是最終傳遞給store的需要是一個Reducer。這里Redux提供了combineReducers方法,把reducers組合成一個傳遞給store。
//合并多個reducer
const rootReducer = combineReducers({ reducer1, reducer2, reducer3});
. Store
Store 就是把 Reducer 和 action 聯(lián)系到一起的橋梁。Store 接收 Action 的數(shù)據(jù)并將其連同當前的 state樹 (包含所有 state 的一種特殊的數(shù)據(jù)結(jié)構(gòu),是一個單一的對象)發(fā)給 Reducer。
Store 有以下職責:
- 維持應用的 state;
- 提供 getState() 方法獲取 state;
- 提供 dispatch(action) 方法更新 state;
- 接收新的state,并替換當前的state;
- state變化時,store觸發(fā)事件;
- 通過 subscribe(listener) 注冊監(jiān)聽器的組件從store提取新的state并更新組件。
Store本質(zhì)上是一個對象,它以樹的形式保存了整個應用的State。并提供了一些方法。例如getState( ) 和 dispatch( )。Redux應用只有惟一一個Store。Store通過createStore方法來創(chuàng)建,根據(jù)整個應用的根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框架應用。但是它和React或者React Native配合得非常完美。原因就是React和React Native的組件都是基于state來渲染視圖的。而Redux正是圍繞著state的管理而構(gòu)建起來的應用框架。
React-Redux是React官方提供的庫。通過這個庫,我們可以很順暢的使用Redux架構(gòu)來構(gòu)建React或React Native應用。github地址。
React-Redux提供了兩個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負責把React Component 和 Redux store 結(jié)合起來。通過connect,你就可以拿到store中的state,并轉(zhuǎn)化成Component的props來使用了
export default connect(state => ({
//counter是reducer的文件名,否則會報返回不是一個對象的錯誤
state: state.count
}),
(dispatch) => ({
//counterActions要包含組件觸發(fā)的action,需要在改組件里導入相應的action
actions: bindActionCreators(actions, dispatch)
})
)(App);
<Provider store>使應用底層的component的connect( )方法能夠獲取到store。通常<Provider store>我們都用來包在整個應用根Component的最外層,這樣保證所有Component都能拿到store
。
Middleware
在redux里,middleware是發(fā)送action和action到達reducer之間的第三方擴展,也就是中間層。也可以這樣說,middleware是架在action和store之間的一座橋梁。如果不使用middleware的話,Redux的store只支持同步數(shù)據(jù)流。也就是每當我們dispatch action時,state會被立即更新。同步只返回一個普通action對象。而異步操作中途會返回一個promise函數(shù)。當然在promise函數(shù)處理完畢后也會返回一個普通action對象。thunk中間件就是判斷如果返回的是函數(shù),則不傳導給reducer,直到檢測到是普通action對象,才交由reducer處理。
使用支持異步的middleware比如 redux-thunk或 redux-promise 能讓我們實現(xiàn)異步的數(shù)據(jù)流。你可以使用applyMiddleware( ) 來增強 createStore( ) 。類似這樣:
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer);
我們也可以編寫自己的中間件,來方便我們Debug,比如打印調(diào)用的action名稱。
引入Redux框架前后組件代碼的對比:
引用前:
'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;
通過對比引用Redux前后對比,大家再慢慢體會一下Redux框架吧!
由于我自己也是剛學習Redux框架,所以還沒有在實際項目中引用,也是參考一些Demo和網(wǎng)上的教程邊參考學習邊體會代碼。在RN中使用Redux看起來很麻煩也很難理解,只要跟著demo去邊敲代碼邊理解就能夠很容易掌握它。我之前也只是一直看博客什么的,但是不用代碼去簡單的實現(xiàn)它,理解起來確實很困難,更別提去實際使用它了。根據(jù)Demo去做可以加深我們的理解,畢竟“紙上得來終覺淺,絕知此事要躬行”嘛!
這里貼出GitHub上實現(xiàn)Redux框架的Demo:
https://github.com/ninty90/react-native-redux-demo
https://github.com/alinz/example-react-native-redux