ReactNative引用Redux框架

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

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

Redux扮演的角色

我們都知道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ě)

根目錄下多出來(lái)的部分

. 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é):

  1. 維持應(yīng)用的 state冤馏;
  2. 提供 getState() 方法獲取 state日麸;
  3. 提供 dispatch(action) 方法更新 state;
  4. 接收新的state逮光,并替換當(dāng)前的state代箭;
  5. state變化時(shí),store觸發(fā)事件涕刚;
  6. 通過(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);

關(guān)于這兩個(gè)API的詳細(xì)說(shuō)明殊者,請(qǐng)參考官方文檔

<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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肃叶,一起剝皮案震驚了整個(gè)濱河市蹂随,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌因惭,老刑警劉巖岳锁,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蹦魔,居然都是意外死亡激率,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)版姑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)柱搜,“玉大人,你說(shuō)我怎么就攤上這事剥险〈险海” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵表制,是天一觀(guān)的道長(zhǎng)健爬。 經(jīng)常有香客問(wèn)我,道長(zhǎng)么介,這世上最難降的妖魔是什么娜遵? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮壤短,結(jié)果婚禮上设拟,老公的妹妹穿的比我還像新娘慨仿。我一直安慰自己,他們只是感情好纳胧,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布镰吆。 她就那樣靜靜地躺著,像睡著了一般跑慕。 火紅的嫁衣襯著肌膚如雪万皿。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,328評(píng)論 1 310
  • 那天核行,我揣著相機(jī)與錄音牢硅,去河邊找鬼。 笑死芝雪,一個(gè)胖子當(dāng)著我的面吹牛减余,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绵脯,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼佳励,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼休里!你這毒婦竟也來(lái)了蛆挫?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤妙黍,失蹤者是張志新(化名)和其女友劉穎悴侵,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拭嫁,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡可免,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了做粤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浇借。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖怕品,靈堂內(nèi)的尸體忽然破棺而出妇垢,到底是詐尸還是另有隱情,我是刑警寧澤肉康,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布闯估,位于F島的核電站,受9級(jí)特大地震影響吼和,放射性物質(zhì)發(fā)生泄漏涨薪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一炫乓、第九天 我趴在偏房一處隱蔽的房頂上張望刚夺。 院中可真熱鬧献丑,春花似錦、人聲如沸侠姑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)结借。三九已至筐摘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間船老,已是汗流浹背咖熟。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柳畔,地道東北人馍管。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像薪韩,于是被迫代替她去往敵國(guó)和親确沸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

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