React Native 使用 Redux

這篇文章是作為一個新手在學(xué)習(xí)過程中對Redux概念的理解拣技。有錯誤的地方希望各位能幫我指出。一起學(xué)習(xí)進步涂炎。

使用React native 開發(fā)的時候烂完,一直在糾結(jié)是否要使用Redux,剛接觸的時候會發(fā)現(xiàn)又有action茄菊,又有Reducer疯潭,還有dispatch,對于我這類剛剛從移動開發(fā)學(xué)習(xí)RN開發(fā)的人面殖,很容易就搞的一團霧水竖哩,學(xué)習(xí)Redux也是看了好幾遍然后又放棄了很多遍,知道公司業(yè)務(wù)需求開發(fā)狀態(tài)比較復(fù)雜的項目的時候脊僚,終于下定決心學(xué)習(xí)使用Redux相叁。
學(xué)習(xí)下來后發(fā)現(xiàn)其實最難入門的是理解Redux的概念和思想,理解后會覺得入門的難度降低很多辽幌。所以分享一下我這個剛?cè)腴T人的體會增淹。

理解Redux

我們在開發(fā)React 或者React native的時候,在一個compentent中乌企,一般的做法是通過state來定義一些可能會引起頁面變化的變量虑润。比如登錄狀態(tài),或者<Text/>中的文字等逛犹。通過改變state狀態(tài)來刷新頁面端辱,state改變的時候會觸發(fā)render來重新渲染該頁面。

如果我們要通過改變state來刷新子控件的話虽画∥璞危可以設(shè)置子控件的Props為當(dāng)前控件的state<SubComponent someProps={this.state.someState} />,通過改變state來控制頁面刷新码撰∩粒或者調(diào)用子控件ref來使用子控件的方法。

然后再跨控件的話脖岛。比如相隔了好幾個頁面朵栖。就只能通過設(shè)置global全局變量或者使用通知監(jiān)聽的方式來實現(xiàn)界面刷新。global是開發(fā)過程中非常不推薦的方式柴梆。而如果一個狀態(tài)的改變要刷新很多其他頁面的UI陨溅。比如登錄成功的狀態(tài)要刷新個人中心頁面其他頁面的頭像或者其他等等時。就會要寫很多的監(jiān)聽绍在。

而且react native中的componentWillMount和iOS中的viewWillAppear不同门扇。切換頁面前后是不會觸發(fā)雹有,componentWillUnmount也相當(dāng)于iOS中的delloca(釋放內(nèi)存時才觸發(fā))。所以必定要使用到很多監(jiān)聽或global臼寄。如果這樣改變頁面多的state多的話霸奕,整個應(yīng)用就會變得非常亂。因此Redux的作用在此時就可以顯現(xiàn)出來了吉拳。

Redux的概念大概是這樣的质帅。把整個應(yīng)用看成一個component的話,這整個應(yīng)用有一個對應(yīng)的this.state留攒。所有頁面都可以拿到這個state中的內(nèi)容煤惩。當(dāng)這個this.state中的state改變的時候。所有關(guān)聯(lián)頁面的頁面就會刷新稼跳。這樣盟庞,不用寫global,不用寫通知汤善。就可以跨頁面共享一個狀態(tài)。畢竟所有redux中的狀態(tài)都是可以全局拿到的票彪。

有點像上面提到的<SubComponent someProps={this.state.someState} />改變state的來改變子控件UI的做法红淡。所有用到rudux的頁面都是他的子控件。改變了state子頁面就都會跟這變降铸。

Store

首先我們要理解Store在旱。這個就理解為所有狀態(tài)的容器就行。它就是套在所有子頁面外的頂級Component推掸。所有的Redux狀態(tài)都是存在這個頂級的component中的this.state桶蝎。它通過<SubComponent someProps={this.state.someState} />這樣的方式來控制子空間的UI。改變了Store中的state就可以實現(xiàn)我們改變UI的操作谅畅。

我們現(xiàn)在把所有數(shù)據(jù)都存在了store中登渣。那么下一步。如果我們要怎么改變Store中的state毡泻。調(diào)用類似Store.setState({...})這樣的操作嗎胜茧?這樣顯得太隨意也太簡單了。畢竟Store中的數(shù)據(jù)可能影響整個程序仇味。如果某個頁面誤改變一下就會引起很多頁面的同時變動呻顽。出了錯誤也很難定位到是哪邊改變出的問題。State應(yīng)該設(shè)置為只讀的丹墨。

所以Store中state這個值不能直接改變廊遍。我們需要一個比較完善的系統(tǒng)來管理所有的state。這時候就該action和reducer出手了贩挣。

Action

reducer規(guī)定喉前,如果想改變這個Store中的狀態(tài)没酣。不能直接改變他的值。而是要要寫一個叫action的東西被饿。
那么action怎么樣簡單的理解呢四康。就是它字面上的意思。一個動作狭握。比如我要登錄闪金。那么登錄成功或者登錄失敗就是一個動作。怎么描述這個動作呢论颅?一般會用下面的一個對象來描述

//定義一個action的種類
const ACTION_TYPE_LOGIN = 'action_type_login';

const loginSuccessAction = {
  type:ACTION_TYPE_LOGIN,
  Result:'success'
}

const loginFailedAction = {
  type:ACTION_TYPE_LOGIN,
  Result:'failed'
}

這樣loginSuccessActionloginFailedAction就分別代表了登錄成功和登錄失敗這個一個動作哎垦。type代表著action的種類。成功和失敗都可以歸結(jié)為登錄這個種類恃疯。 type是必須要的參數(shù)漏设,后面的Reducer會根據(jù)這個屬性來判斷改變哪部分的狀態(tài)。

dispatch

字面上的意思是部署今妄。部署什么呢郑口?部署一個動作。也就是告訴Redux盾鳞。我登錄成功了犬性。你趕緊處理一下改變相應(yīng)State吧。告訴Redux腾仅。使用起來就是調(diào)用dispatch(loginSuccessAction),然后乒裆,Redux就知道你登錄成功了。他就會讓一個叫Reducer的人來處理你這個動作推励。

Reducer

我們又碰到了一個新的名詞Reducer鹤耍。這是什么東西呢?Reducer可以理解為通過接收action動作验辞,然后來改變我們的全局狀態(tài)的一個處理者稿黄。

通過dispatch(loginSuccessAction)來告訴Redux,我登錄成功了受神。然后rudux就讓Ruducer來處理這個登錄成功動作抛猖,Reducer拿到了登錄成功這個動作,同時也拿到了Store中當(dāng)前的狀態(tài)鼻听。通過這兩個對象财著。生成了一個登錄成功后Store應(yīng)該變成的狀態(tài)。

用代碼表示就是這樣的

function loginReducer (state,action){
  switch (action.type) {
        case TYPES.ACTION_LOGIN: // 初始狀態(tài)
            return Object.assign({}, state, {
                result:action.Result
            });
        case ...: // 初始狀態(tài)
        default:
            return state;
}

這里要注意的是Reducer并不是用來改變Store中的狀態(tài)的撑碴。他是通過Action來生成一個新的狀態(tài)的撑教。然后return 給了Store,Store自己再替換自己的狀態(tài)醉拓。

所以總結(jié)下來各一句話就是
Store:狀態(tài)容器
Action:對于一個動作的描述伟姐,是一個對象收苏。
dispatch:實施動作的方法。
reducer:接收一個action和Store的當(dāng)前狀態(tài)愤兵,返回新的Store該有狀態(tài)的處理者鹿霸。
Redux的最基本的概念差不多就是這樣。

稍作進階

上面的介紹完后秆乳,應(yīng)該對Rudux的概念有了一些初步理解懦鼠。那么我們要應(yīng)用到項目中,還需要一些性能的處理和優(yōu)化屹堰。

中間件Middleware

如果按照上面的步揍肛冶,我們完成登錄操作要寫多少個Action呢?至少有這些吧
登錄成功扯键,登錄失敗睦袖,登錄錯誤,登錄中荣刑。那我們就要寫四個Action馅笙。那整個項目要寫的action會突破天際吧。這時候厉亏。對于屬于一個type的action延蟹。我們可以把他們四合一,寫一個生成action的方法然后執(zhí)行就行.

function loginAction(result){
  return {
    type:ACTION_LOGIN,
    result:result
  }
}

然后調(diào)用store.dispatch(loginAction(result))就可以了。
那么我們登錄過程要在component中通過登錄的result來dispatch好幾次叶堆。可不可以把判斷的過程也剝離出來呢斥杜?比如整個登錄我只會在component中調(diào)用一次store.dispatch(login())虱颗,后面所有的登錄結(jié)果的改變?nèi)疾辉陧撁胬锾幚怼_@樣頁面就可以只負責(zé)UI蔗喂。狀態(tài)的改變完全交給Redux忘渔。

login預(yù)想的方法是這樣

function login(){
  //這里就沒法返回登錄中的action

  fetch(url).then(result=>{
    return{
      type:action_login,
      result:result
    }
  }).catch(err=>{
    return{
      type:action_login,
      result:'error'
    }
  })
}

但是store.dispatch()默認是只接收一個Action對象。而我們login()方法中肯定要涉及到異步操作缰儿。action對象不能立即返回出來畦粮。store.dispatch(login())在執(zhí)行的時 login()不能直接有返回值。相當(dāng)于執(zhí)行了store.dispatch(null)乖阵。而且登錄的整個流程要返回登錄中+登錄結(jié)果兩個action宣赔,這樣構(gòu)造肯定沒法實現(xiàn)。怎么解決這個問題呢瞪浸?
如果我們能在login()中執(zhí)行其他的dispatch()動作是不是就迎刃而解了儒将。
如果能寫成這樣

function login(){
//返回一個方法
  return dispath=>{
    dispatch(login(loginAction('logging in')));
    fetch(url).then(result=>{
      dispatch(login(loginAction(result)));
    }).catch(err=>{
      dispatch(login(loginAction('error')));
    })
  }
}
function loginAction(result){
  return {
    type:ACTION_LOGIN,
    result:result
  }
}

但是redux默認是不允許這么寫的。因為 return的 dispath=>{...}是一個方法而不是對象对蒲。我們可以通過安裝一個中間件redux-thunk來實現(xiàn)讓store.dispatch()可以接收一個方法钩蚊。而不是只接收action對象贡翘。

出現(xiàn)了一個新名詞,中間件(middleware)砰逻。

Redux的流程其實很簡單
store.dispatch(action) -> reducer處理action鸣驱,返回一個新的state ->Store更新state ->相關(guān)UI更新。

middleware可以認為就是改造store.dispatch()這個方法的蝠咆。他讓你在執(zhí)行部署action的過程中踊东,加入一些自己的處理。比如redux-logger這個中間件可以用來添加日志功能勺美,在dispatch()時打印出你所執(zhí)行的所有動作递胧。redux-trun可以讓dispatch()接受一個function。redux-promise可以讓其接收一個promise.

模塊分割來優(yōu)化性能

在component中,如果改變this.state中的任意一個狀態(tài)窝稿,都會引發(fā)頁面的render渲染拔鹰。

Redux在一個應(yīng)用中只允許有一個Store 容器來存儲State。那么所有的State就會都在這個Store中遗菠。也就是說。如果改變了state的話华蜒。整個應(yīng)用中用到store中的state的頁面都會隨之刷新辙纬。可想而知叭喜。不進行優(yōu)化的話贺拣,必定會有很大的性能問題。

想到的第一個方法是shouldComponentUpdate攔截不必要的Render捂蕴。

 shouldComponentUpdate(nextProps,nextState){
      if(nextProps.ReduxState.loginResult != this.Props.ReduxState.loginResult){
        return true
      }
      return false
  }

但是這樣的話需要我們逐個判斷props改變是否跟新頁面譬涡。全都這樣判斷稍大點的應(yīng)用得寫幾千行代碼。

這時候啥辨。我們需要把Rudux分模塊切割開來涡匀。每個小模塊只負責(zé)他負責(zé)的事情。UI的component用到哪個模塊就把這個模塊和它關(guān)聯(lián)起來溉知。

切割模塊

import {combineReducers} from "redux";
import ...
const rootReducer = combineReducers({
    LoginReducer,
    PageManagerReducer,
    MusicManagerReducer
    ...
});

然后UI 頁面中中


class Page extends React.Component {
  login = ()=>{
    //調(diào)用Action的方法
    this.props.dispatch(...someAction)
  }
  render(
    <View>
      <Text>{this.props.LoginReducer.someState}</Text>
    </View>
  )

}
const useReducers = store => {
    return {
        //store.LoginReducer就是此頁面使用到的Reducer 就是rootReducer中的模塊名陨瘩,前面的LoginReducer代表的是頁面使用時this.props中的參數(shù)名。
        LoginReducer: store.LoginReducer,
    };
};
//關(guān)聯(lián)頁面和用到的模塊,關(guān)聯(lián)后级乍,store就會把子模塊的State通過props傳遞給此頁面舌劳。this.props
export default connect(useReducers)(Page);

這樣UI頁面中,只有在LoginReducer子模塊中的狀態(tài)發(fā)生改變時卡者,頁面才會刷新蒿囤。

好了,掌握這些東西崇决。我們就可以開始用Redux寫React native應(yīng)用了材诽。
中間有很多代碼示例沒有寫底挫。晚點直接寫個Demo吧。

學(xué)習(xí)的時候推薦阮一峰老師的Redux系列教程脸侥。實在和我一樣概念不清的再來看看我個人的描述吧建邓。
傳送門:http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市睁枕,隨后出現(xiàn)的幾起案子官边,更是在濱河造成了極大的恐慌,老刑警劉巖外遇,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件注簿,死亡現(xiàn)場離奇詭異,居然都是意外死亡跳仿,警方通過查閱死者的電腦和手機诡渴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來菲语,“玉大人妄辩,你說我怎么就攤上這事∩缴希” “怎么了眼耀?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長佩憾。 經(jīng)常有香客問我哮伟,道長,這世上最難降的妖魔是什么妄帘? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任澈吨,我火速辦了婚禮,結(jié)果婚禮上寄摆,老公的妹妹穿的比我還像新娘。我一直安慰自己修赞,他們只是感情好婶恼,可當(dāng)我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著柏副,像睡著了一般勾邦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上割择,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天眷篇,我揣著相機與錄音,去河邊找鬼荔泳。 笑死蕉饼,一個胖子當(dāng)著我的面吹牛虐杯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播昧港,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼擎椰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了创肥?” 一聲冷哼從身側(cè)響起达舒,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎叹侄,沒想到半個月后巩搏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡趾代,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年贯底,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稽坤。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡丈甸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尿褪,到底是詐尸還是另有隱情睦擂,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布杖玲,位于F島的核電站顿仇,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏摆马。R本人自食惡果不足惜臼闻,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望囤采。 院中可真熱鬧述呐,春花似錦、人聲如沸蕉毯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽代虾。三九已至进肯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棉磨,已是汗流浹背江掩。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人环形。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓策泣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親斟赚。 傳聞我的和親對象是個殘疾皇子着降,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,658評論 2 350

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