ReactNative引用Redux框架

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框架圖

不像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改變操作。

Redux扮演的角色

我們都知道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 有以下職責:

  1. 維持應用的 state;
  2. 提供 getState() 方法獲取 state;
  3. 提供 dispatch(action) 方法更新 state;
  4. 接收新的state,并替換當前的state;
  5. state變化時,store觸發(fā)事件;
  6. 通過 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);

關(guān)于這兩個API的詳細說明,請參考官方文檔

<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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內(nèi)容