React Native - 從 Redux 進(jìn)階談起

Reeds in the Autumn Wind

繼上次寫(xiě)完 Redux 之后壶唤,留下了很多坑,其實(shí)這篇也不算是進(jìn)階幢痘,畢竟只是一些庫(kù)的使用以及一些小技巧而已,權(quán)當(dāng)是上一篇的填坑了吧皆看。

01 Middleware & Thunk

What's Middlewares?

當(dāng)我們發(fā)送 action 的時(shí)候仓坞,正常情況下是從 action -> reducer,引入中間件后變?yōu)?action -> middlewares -> reducer腰吟。為什么要這么做扯躺?因?yàn)楹芏鄷r(shí)候我們想在 action 傳送到 reducer 之前,對(duì)數(shù)據(jù)流進(jìn)行改變或者進(jìn)行一些別的什么操作蝎困,比如對(duì) action 添加日志录语,如果一個(gè)個(gè)去更改 Action Creator 未免太過(guò)麻煩,而使用 Middleware 的話就簡(jiǎn)單很多禾乘,可以幫我們省去很多重復(fù)代碼澎埠。最重要的一點(diǎn),我們可以通過(guò)使用 Thunk Middleware 來(lái)實(shí)現(xiàn)異步 action始藕。

What’s a thunk?

Thunk 就是包裝了函數(shù)表達(dá)式的用于延緩求值的方法蒲稳。通過(guò)下面這個(gè)例子可以很好的理解:

// 立即計(jì)算求值 1+2
let x = 1 + 2;

// 計(jì)算被延遲了,foo 可以在稍后被調(diào)用的時(shí)候再去計(jì)算 1+2伍派,這個(gè)時(shí)候 foo 方法就是一個(gè) thunk 方法
let foo = () => 1 + 2;

為什么叫 thunk江耀?它是『think』過(guò)去式的 幽默式表達(dá)(意思是已經(jīng)想好怎么做了,但是就是還沒(méi)做 o(′^`)o)诉植。

Redux Thunk

當(dāng)我們使用了 middleware 的時(shí)候祥国,發(fā)出的 action 不會(huì)直接被 reducer 處理掉,而是先被 middleware 截獲晾腔,并且我們可以在 middleware 中發(fā)起 異步請(qǐng)求舌稀。Redux Thunk 就是這樣的中間件,允許我們?cè)?Redux 中發(fā)起異步請(qǐng)求灼擂。

applyMiddleware

createStore() 的時(shí)候壁查,我們可以指定 enhancer 參數(shù),最常見(jiàn)的就是 applyMiddleware()剔应,為了在 Redux 中開(kāi)啟對(duì) Redux Thunk 的支持睡腿,就需要使用該方法:

import {applyMiddleware, combineReducers, createStore} from "redux";
import thunkMiddleware from 'redux-thunk';
import textReducer from "./reducers/changeText";

const allReducers = combineReducers({textReducer});
let store = createStore(allReducers, applyMiddleware(thunkMiddleware));

export default store;
Return a function

使用了 Redux Thunk 后,我們可以在 Action Creators 中返回一個(gè)方法而不是 action峻贮,因此我們可以延遲 action 的 dispatch 或者在只有滿(mǎn)足條件的時(shí)候才 dispatch席怪。內(nèi)部返回的方法接收的參數(shù)分別是 store 的 dispatchgetState 方法。舉個(gè)發(fā)起請(qǐng)求獲取數(shù)據(jù)的例子:

export const FETCH_DATA_IN_PROGRESS = 'FETCH_DATA_IN_PROGRESS';
export const FETCH_DATA_SUCCESS = 'FETCH_DATA_SUCCESS';
export const FETCH_DATA_Failure = 'FETCH_DATA_Failure';

function getData(url) {
  return async (dispatch, getState) => {
    
    let param = getState().getData.param;
    
    dispatch({type: FETCH_DATA_IN_PROGRESS});

    // request 是封裝的 fetch 請(qǐng)求
    const res = await request.post(url, {param});

    if (res && !res.status) {
      // 請(qǐng)求成功才發(fā)送數(shù)據(jù)
      dispatch({type: FETCH_DATA_SUCCESS, data: res.data});
    } else {
      dispatch({type: FETCH_DATA_Failure});
    }
  }
}

上面這個(gè)就是一個(gè) thunk function月洛,其使用方式與普通的 Action Creators 一樣何恶,可以直接作為第二個(gè)參數(shù)傳入 connect() 方法中:

export default connect(mapStateToProps, {changeText, changeBack, getData})(Main);

當(dāng)然 Redux Thunk 也不是唯一的發(fā)送異步 Action 的方式孽锥,比如 redux-saga 以及 redux-promise 都可以達(dá)到相同的目的嚼黔,Redux 文檔上還介紹了一些別的方式:

Thunk middleware isn't the only way to orchestrate asynchronous actions in Redux:

It is up to you to try a few options, choose a convention you like, and follow it, whether with, or without the middleware.

02 Higher-Order Components

What's HOC?

HOC 即 Higher-Order Components 高階組件的簡(jiǎn)稱(chēng)细层,從形式上來(lái)看,其實(shí)就是接收函數(shù)作為參數(shù)的函數(shù)唬涧。

HOC 是 React 中的一種模式疫赎,通過(guò) HOC 我們可以方便地在多個(gè)組件中注入一些通用的功能,這樣就可以避免重復(fù)的代碼邏輯碎节。一個(gè) HOC 函數(shù)接收一個(gè)組件作為參數(shù)捧搞,并且返回一個(gè)新的組件,通過(guò) HOC 函數(shù)我們可以為組件添加額外的功能或者數(shù)據(jù)狮荔。

在 RN 中胎撇,一種常見(jiàn)的使用方式是通過(guò) HOC 函數(shù)作為頁(yè)面跳轉(zhuǎn)的依據(jù)。比如檢驗(yàn)有無(wú)登錄殖氏,如果頁(yè)面需要登錄后才能查看晚树,那么用戶(hù)在未登錄的情況下會(huì)先跳轉(zhuǎn)登錄頁(yè)。

How to use it?

HOC 的使用方法很簡(jiǎn)單雅采,形式如下:

const hoc = (WrappedComponent) => {
  class HOC extends React.Component {
    render() {
      return <WrappedComponent />;
    }
  }
  return HOC;
};

這里的匿名函數(shù)接收的參數(shù)是 WrappedComponent爵憎,即我們需要包裝的組件,返回的是我們對(duì)包裝的組件進(jìn)行處理之后的新組件婚瓜。其使用方式如下:

const myHOC = hoc(MyComponent);

除此之外宝鼓,我們也可以結(jié)合 React Redux 使用:

const hoc = (WrappedComponent) => connect(mapStateToProps, mapDispatchToProps)(
  class HOC extends React.Component {
    render() {
      return <WrappedComponent />;
    }
  }
 );

其實(shí)這里的 connect() 方法就是一個(gè) HOC 的例子,通過(guò)連接組件和保存在 Store 中的全局 state巴刻,同時(shí)在組件中可以通過(guò) props 的形式來(lái)訪問(wèn)這些全局 state愚铡。

03 Combine with react-native-router-flux

介紹了 Thunk Middleware 和 HOC,接下來(lái)我想用一個(gè)例子展示如何在項(xiàng)目中使用他們胡陪。這個(gè)例子使用到了 React Native Router茂附,這是一個(gè)非常好用的頁(yè)面路由、頁(yè)面導(dǎo)航以及頁(yè)面間傳遞數(shù)據(jù)的 RN 框架督弓。這里我只用到了其中很小一部分的功能营曼,更多的用法請(qǐng)移步 API 文檔

照慣例愚隧,先看下實(shí)現(xiàn)的效果:Demo GIF

可以看到蒂阱,我們?cè)谠瓉?lái)的基礎(chǔ)上添加了兩個(gè)新的頁(yè)面,一個(gè)是登錄后的頁(yè)面狂塘,一個(gè)是登錄頁(yè)录煤,連接它們的是一個(gè) HOC 函數(shù):

const verifyLogin = WrappedComponent => connect(mapStateToProps)(
  class extends Component {
    render() {
      if (this.props.authToken) {
        return (<WrappedComponent {...this.props} />);
      } else {
        return (<Login/>);
      }
    }
  }
);

function mapStateToProps(state) {
  return {
    authToken: state.authInfo.data,
  };
}

export default verifyLogin;

在 HOC 中,我們根據(jù) authToken 的狀態(tài)來(lái)決定是直接跳轉(zhuǎn)還是先跳轉(zhuǎn)到登錄頁(yè)荞胡。這個(gè)例子很好的說(shuō)明了 HOC 函數(shù)的優(yōu)勢(shì)妈踊,所有需要登錄的地方都只要調(diào)用這個(gè)函數(shù)就可以了。這里的 authToken 是從 store 中獲取的泪漂,因此我們還得寫(xiě)一個(gè)模擬登錄的 Action Creator 來(lái)進(jìn)行登錄獲取 token廊营,同時(shí)展示下如何使用 Redux Thunk 發(fā)起異步請(qǐng)求:

export function login(info) {
  return async (dispatch, getState) => {
    dispatch({type: Request_login_requesting});

    // 模擬發(fā)起請(qǐng)求并獲取結(jié)果
    let res = 'fakeAuthRequestAndGetResult';

    // 根據(jù)請(qǐng)求結(jié)果發(fā)送不同的 action
    if (res) {
      dispatch({type: Request_login_success, data: res});
    } else {
      dispatch({type: Request_login_failure});
    }
  }
}

另外歪泳,還有對(duì)應(yīng)的 reducer,注意露筒,combineReducers() 接收的參數(shù)為對(duì)象:

function data(state = '', actions) {
  switch(actions.type){
    case Request_login_success:
    case Request_login_failure:
      return actions.data;
    case Request_logout:
      return '';
    default:
      return state;
  }
}

let authInfo = combineReducers({data});

export default authInfo;

最后呐伞,除了兩個(gè)新頁(yè)面之外,我們還需要定義一個(gè) router 頁(yè)慎式,也就是使用 React Native Router 來(lái)管理各個(gè)頁(yè)面:

class AppRouter extends Component {
  render() {
    return <Router>
      <Stack>
        <Scene key='root' component={Main} />
        <Scene key='personal' component={Personal} />
        <Scene key='login' component={Login} />
      </Stack>
    </Router>
  }
}

export default AppRouter;

然后在 App 的入口處使用 AppRouter 替換原來(lái)的 Main:

export default class Root extends Component {
  render() {
    return (
      // 第一層包裝伶氢,連接組件和 store
      <Provider store={store}>
        <AppRouter/>
      </Provider>
    )
  }
}

OK,核心代碼就是這樣了瘪吏,完整代碼:aJIEw/Redux

04 Sum Up

這篇寫(xiě)的比較雜癣防,一開(kāi)始只是想寫(xiě)下 Redux 中的 middleware,后來(lái)看了這篇 Redux-Thunk vs. Redux-Saga掌眠,發(fā)現(xiàn)對(duì)于大多數(shù)場(chǎng)景下劣砍,的確使用 Redux-Thunk 就足夠了。而 HOC 也是臨時(shí)想到要寫(xiě)一寫(xiě)的扇救,畢竟也算是 React 中一種常見(jiàn)的模式了吧刑枝。最后這個(gè)結(jié)合 react-native-router 寫(xiě)的例子也比較簡(jiǎn)單,主要用來(lái)說(shuō)明 Redux-Thunk 發(fā)異步請(qǐng)求以及 HOC 的大致用法迅腔。

好了装畅,寫(xiě)完這篇有種『我已經(jīng)掌握 React Native 開(kāi)發(fā)了』的錯(cuò)覺(jué),但其是內(nèi)心還是很慌的沧烈,因?yàn)橹酪莆盏臇|西還有太多掠兄。不過(guò)一口吃不成個(gè)胖子,只能靜下心來(lái)一步一腳印慢慢往前走了锌雀。每天進(jìn)步一點(diǎn)點(diǎn)蚂夕,堅(jiān)持下去,收獲就是巨大的腋逆。嗯婿牍,加油~(?????)? ??


相關(guān)文章:React Native - Redux 入門(mén)

參考文章:
  1. React 項(xiàng)目中 Redux 中間件的理解
  2. Redux 入門(mén)教程(二):中間件與異步操作
  3. Redux-Thunk 快速入門(mén)
  4. Understanding how redux-thunk works
  5. 面向初學(xué)者的高階組件教程
  6. Understanding React Higher-Order Components by Example
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市惩歉,隨后出現(xiàn)的幾起案子等脂,更是在濱河造成了極大的恐慌,老刑警劉巖撑蚌,帶你破解...
    沈念sama閱讀 223,002評(píng)論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件上遥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡争涌,警方通過(guò)查閱死者的電腦和手機(jī)粉楚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人模软,你說(shuō)我怎么就攤上這事伟骨。” “怎么了撵摆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,787評(píng)論 0 365
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)害晦。 經(jīng)常有香客問(wèn)我特铝,道長(zhǎng),這世上最難降的妖魔是什么壹瘟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,237評(píng)論 1 300
  • 正文 為了忘掉前任鲫剿,我火速辦了婚禮,結(jié)果婚禮上稻轨,老公的妹妹穿的比我還像新娘灵莲。我一直安慰自己,他們只是感情好殴俱,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布政冻。 她就那樣靜靜地躺著,像睡著了一般线欲。 火紅的嫁衣襯著肌膚如雪明场。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,821評(píng)論 1 314
  • 那天李丰,我揣著相機(jī)與錄音苦锨,去河邊找鬼。 笑死趴泌,一個(gè)胖子當(dāng)著我的面吹牛舟舒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嗜憔,決...
    沈念sama閱讀 41,236評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼秃励,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了吉捶?” 一聲冷哼從身側(cè)響起莺治,我...
    開(kāi)封第一講書(shū)人閱讀 40,196評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帚稠,沒(méi)想到半個(gè)月后谣旁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,716評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡滋早,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評(píng)論 3 343
  • 正文 我和宋清朗相戀三年榄审,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杆麸。...
    茶點(diǎn)故事閱讀 40,928評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡搁进,死狀恐怖浪感,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饼问,我是刑警寧澤影兽,帶...
    沈念sama閱讀 36,583評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站莱革,受9級(jí)特大地震影響峻堰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盅视,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評(píng)論 3 336
  • 文/蒙蒙 一捐名、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧闹击,春花似錦镶蹋、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,755評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)汇四。三九已至铺敌,卻和暖如春象泵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瑰枫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,869評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工踱葛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人光坝。 一個(gè)月前我還...
    沈念sama閱讀 49,378評(píng)論 3 379
  • 正文 我出身青樓尸诽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親盯另。 傳聞我的和親對(duì)象是個(gè)殘疾皇子性含,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評(píng)論 2 361

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