GitHub Popular App Redux

目錄

  • 什么是Redux
  • Redux 優(yōu)點(diǎn)
  • Redux 的三個(gè)基本原則
  • Redux 有那幾部分組成
  • Action
  • reducer
  • Store
  • 異步Action
  • 配置React Native與Redux開(kāi)發(fā)
    • 第一步
    • 第二步
    • 第三步
image.png

什么是Redux

Redux 是 JavaScript 狀態(tài)容器骗炉,提供可預(yù)測(cè)化的狀態(tài)管理,可以讓你構(gòu)建一致化的應(yīng)用振惰,運(yùn)行于不同的環(huán)境(客戶(hù)端睹耐、服務(wù)器嵌削、原生應(yīng)用),并且易于測(cè)試。數(shù)據(jù)是單向流動(dòng)的。


1

工作流程

  1. 用戶(hù)(操作View)發(fā)出Action聪铺,發(fā)出方式就用到了dispatch方法;
  2. 然后萄窜,Store自動(dòng)調(diào)用Reducer铃剔,并且傳入兩個(gè)參數(shù)(當(dāng)前State和收到的Action)撒桨,Reducer會(huì)返回新的State,如果有Middleware键兜,Store會(huì)將當(dāng)前State和收到的Action傳遞給Middleware凤类,Middleware會(huì)調(diào)用Reducer 然后返回新的State;
  3. State一旦有變化普气,Store就會(huì)調(diào)用監(jiān)聽(tīng)函數(shù)谜疤,來(lái)更新View;

Middleware:可以讓你在reducer執(zhí)行前與執(zhí)行后進(jìn)行攔截并插入代碼现诀,來(lái)達(dá)到操作action和Store的目的夷磕,這樣一來(lái)就很容易實(shí)現(xiàn)靈活的日志打印、錯(cuò)誤收集仔沿、API請(qǐng)求坐桩、路由等操作。

Redux優(yōu)點(diǎn)

  • 可預(yù)測(cè): 始終有一個(gè)唯一的準(zhǔn)確的數(shù)據(jù)源(single source of truth)就是store封锉,通過(guò)actions和reducers來(lái)保證整個(gè)應(yīng)用狀態(tài)同步绵跷,做到絕不混亂
  • 易維護(hù): 具備可預(yù)測(cè)的結(jié)果和嚴(yán)格的組織結(jié)構(gòu)讓代碼更容易維護(hù)
  • 易測(cè)試: 編寫(xiě)可測(cè)試代碼的首要準(zhǔn)則是編寫(xiě)可以?xún)H做一件事并且獨(dú)立的小函數(shù)(single responsibility principle),Redux的代碼幾乎全部都是這樣的函數(shù):短小·純粹·分離

Redux 的三個(gè)基本原則

  • 單一數(shù)據(jù)源:整個(gè)應(yīng)用的 state 被儲(chǔ)存在一棵 object tree 中成福,并且這個(gè) object tree 只存在于唯一一個(gè) store 中碾局;
  • State 是只讀的:唯一改變 state 的方法就是觸發(fā) action,action 是一個(gè)用于描述已發(fā)生事件的普通對(duì)象奴艾;
  • 使用純函數(shù)來(lái)執(zhí)行修改:為了描述 action 如何改變 state tree 净当,你需要編寫(xiě) reducers;

Redux 有那幾部分組成

action:action就是一個(gè)描述發(fā)生什么的對(duì)象蕴潦;
reducer:形式為 (state, action) => state 的純函數(shù)蚯瞧,功能是根據(jù)action 修改state 將其轉(zhuǎn)變成下一個(gè) state;
store:用于存儲(chǔ)state品擎,你可以把它看成一個(gè)容器埋合,整個(gè)應(yīng)用只能有一個(gè)store。


Action

Action 是把數(shù)據(jù)從應(yīng)用傳到 store的有效載荷萄传。它是 store數(shù)據(jù)的唯一來(lái)源畦浓,也就是說(shuō)要改變store中的state就需要觸發(fā)一個(gè)action龄句。

Action 本質(zhì)上一個(gè)普通的JavaScript對(duì)象誊酌。action 內(nèi)必須使用一個(gè)字符串類(lèi)型的type字段來(lái)表示將要執(zhí)行的動(dòng)作戚嗅,除了 type 字段外,action 對(duì)象的結(jié)構(gòu)完全由你自己決定衍菱。多數(shù)情況下赶么,type 會(huì)被定義成字符串常量。當(dāng)應(yīng)用規(guī)模越來(lái)越大時(shí)脊串,建議使用單獨(dú)的模塊或文件來(lái)存放 action辫呻。

import { ADD_TODO, REMOVE_TODO } from '../actionTypes'

//action
{
  type: ADD_TODO,
  text: 'Build my first Redux app'
} 

提示:使用單獨(dú)的模塊或文件來(lái)定義 action type 常量并不是必須的清钥,甚至根本不需要定義。對(duì)于小應(yīng)用來(lái)說(shuō)放闺,使用字符串做 action type 更方便些祟昭。不過(guò),在大型應(yīng)用中把它們顯式地定義成常量還是利大于弊的怖侦。

reducer

reducer是根據(jù)action 修改state 將其轉(zhuǎn)變成下一個(gè) state篡悟,記住 actions 只是描述了有事情發(fā)生了這一事實(shí),并沒(méi)有描述應(yīng)用如何更新 state匾寝。

(previousState, action) => newState

保持 reducer 純凈非常重要搬葬。永遠(yuǎn)不要在 reducer 里做這些操作

  • 修改傳入?yún)?shù);
  • 執(zhí)行有副作用的操作艳悔,如 API 請(qǐng)求和路由跳轉(zhuǎn)急凰;
  • 調(diào)用非純函數(shù),如 Date.now() 或 Math.random()很钓。
    提示:reducer 是純函數(shù)。它僅僅用于計(jì)算下一個(gè) state董栽。它應(yīng)該是完全可預(yù)測(cè)的:多次傳入相同的輸入必須產(chǎn)生相同的輸出码倦。它不應(yīng)做有副作用的操作,如 API 調(diào)用或路由跳轉(zhuǎn)锭碳。這些應(yīng)該在 dispatch action 前發(fā)生袁稽。

拆分reducer:根據(jù)不同模塊的劃分去查分reducer,減少冗長(zhǎng)擒抛,讓代碼簡(jiǎn)潔推汽。
合并reducer:經(jīng)過(guò)上述的步驟我們將一個(gè)大的reducer拆分成了不同的小的reducer,但redux原則是只允許一個(gè)根reducer歧沪,接下來(lái)我們需要將這幾個(gè)小的reducer聚合到一個(gè)跟reducer中歹撒。

這里我們需要用到Redux 提供的combineReducers(reducers)
combineReducers() 所做的只是生成一個(gè)函數(shù)诊胞,這個(gè)函數(shù)來(lái)調(diào)用你的一系列 reducer暖夭,每個(gè) reducer 根據(jù)它們的 key 來(lái)篩選出 state 中的一部分?jǐn)?shù)據(jù)并處理,然后這個(gè)生成的函數(shù)再將所有 reducer 的結(jié)果合并成一個(gè)大的對(duì)象撵孤。沒(méi)有任何魔法迈着。正如其他 reducers,如果 combineReducers()中包含的所有 reducers 都沒(méi)有更改 state邪码,那么也就不會(huì)創(chuàng)建一個(gè)新的對(duì)象

Store

是存儲(chǔ)state的容器裕菠,Store 會(huì)把兩個(gè)參數(shù)(當(dāng)前的 state 樹(shù)和 action)傳入 reducer。

store 有以下職責(zé):

  • 維持應(yīng)用的 state闭专;
  • 提供 getState() 方法獲取 state奴潘;
  • 提供 dispatch(action)方法更新 state:我們可以在任何地方調(diào)用 store.dispatch(action)旧烧,包括組件中、XMLHttpRequest 回調(diào)中萤彩、甚至定時(shí)器中粪滤;
  • 通過(guò) subscribe(listener) 注冊(cè)監(jiān)聽(tīng)器;
  • 通過(guò) subscribe(listener) 返回的函數(shù)注銷(xiāo)監(jiān)聽(tīng)器。

我們使用 combineReducers() 將多個(gè) reducer 合并成為一個(gè)∪阜觯現(xiàn)在我們通過(guò)Redux的 createStore()來(lái)創(chuàng)建一個(gè)Store杖小。

import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)

異步Action

上文中所講的Action都是基于同步實(shí)現(xiàn)的,那么對(duì)于網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)庫(kù)加載等應(yīng)用場(chǎng)景同步Action顯然是不適用的愚墓,對(duì)此我們需要用到異步Action予权。
我們可將異步Action簡(jiǎn)答理解為:在Action中進(jìn)行異步操作等操作返回后再dispatch一個(gè)action。

為了使用異步action我們需要引入redux-thunk庫(kù)浪册,redux-thunk是為Redux提供異步action支持的中間件扫腺。

使用redux-thunk

npm install --save redux-thunk

import thunk from 'redux-thunk'
let middlewares = [
    thunk
]
//添加異步中間件redux-thunk
let createAppStore = applyMiddleware(...middlewares)(createStore)

創(chuàng)建異步Action

export function onSearch(inputKey, token, popularKeys) {
    return dispatch => {
        dispatch({type: Types.SEARCH_REFRESH});
        fetch(genFetchUrl(inputKey)).then(response => {//如果任務(wù)取消,則不做任何處理
            return checkCancel(token) ? response.json() : null;
        }).then(responseData => {
            if (!checkCancel(token, true)) {//如果任務(wù)取消村象,則不做任何處理
                return
            }
            if (!responseData || !responseData.items || responseData.items.length === 0) {
                dispatch({type: Types.SEARCH_FAIL, message: inputKey + '什么都沒(méi)找到'});
                return
            }
            let items = responseData.items;
            getFavoriteKeys(inputKey, dispatch, items, token, popularKeys);
        }).catch(e => {
            console.log(e);
            dispatch({type: Types.SEARCH_FAIL, error: e});
        })
    }
}

異步數(shù)據(jù)流

默認(rèn)情況下笆环,createStore() 所創(chuàng)建的 Redux store 沒(méi)有使用 middleware,所以只支持 同步數(shù)據(jù)流厚者。

你可以使用 applyMiddleware() 來(lái)增強(qiáng) createStore()躁劣。它可以幫助你用簡(jiǎn)便的方式來(lái)描述異步的 action。

像 redux-thunk 或 redux-promise 這樣支持異步的 middleware 都包裝了 store 的 dispatch() 方法库菲,以此來(lái)讓你 dispatch 一些除了 action 以外的其他內(nèi)容账忘,例如:函數(shù)或者 Promise。你所使用的任何 middleware 都可以以自己的方式解析你 dispatch 的任何內(nèi)容熙宇,并繼續(xù)傳遞 actions 給下一個(gè) middleware鳖擒。比如,支持 Promise 的 middleware 能夠攔截 Promise烫止,然后為每個(gè) Promise 異步地 dispatch 一對(duì) begin/end actions蒋荚。

當(dāng) middleware 鏈中的最后一個(gè) middleware 開(kāi)始 dispatch action 時(shí),這個(gè) action 必須是一個(gè)普通對(duì)象馆蠕;


配置React Native與Redux開(kāi)發(fā)

第一步:在使用 React Navigation 的項(xiàng)目中圆裕,想要集成 redux 就必須要引入 react-navigation-redux-helpers 這個(gè)庫(kù)。

第二步:配置Navigator

/**
 * 1.初始化react-navigation與redux的中間件荆几,
 * 該方法的一個(gè)很大的作用就是為reduxifyNavigator的key設(shè)置actionSubscribers(行為訂閱者)
 * 設(shè)置訂閱者@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L29
 * 檢測(cè)訂閱者是否存在@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L97
 * @type {Middleware}
 */
export const middleware = createReactNavigationReduxMiddleware(
    'root',
    state => state.nav
);

/**
 * 2.將根導(dǎo)航器組件傳遞給 reduxifyNavigator 函數(shù),
 * 并返回一個(gè)將navigation state 和 dispatch 函數(shù)作為 props的新組件吓妆;
 * 注意:要在createReactNavigationReduxMiddleware之后執(zhí)行
 */
const AppWithNavigationState = reduxifyNavigator(RootNavigator, 'root');

/**
 * State到Props的映射關(guān)系
 * @param state
 */
const mapStateToProps = state => ({
    state: state.nav,//v2
});
/**
 * 3.連接 React 組件與 Redux store
 */
export default connect(mapStateToProps)(AppWithNavigationState);

第三步配置Reducer

//1.指定默認(rèn)state
const navState = RootNavigator.router.getStateForAction(RootNavigator.router.getActionForPathAndParams(rootCom));

/**
 * 2.創(chuàng)建自己的 navigation reducer,
 */
const navReducer = (state = navState, action) => {
    const nextState = RootNavigator.router.getStateForAction(action, state);
    // 如果`nextState`為null或未定義吨铸,只需返回原始`state`
    return nextState || state;
};

/**
 * 3.合并reducer
 * @type {Reducer<any> | Reducer<any, AnyAction>}
 */
const index = combineReducers({
    nav: navReducer,
    theme: theme,
    popular: popular,
    trending: trending,
    favorite: favorite,
    language: language,
    search: search,
});

export default index;

第四步:配置store

/**
 * 自定義log中間件
 * https://cn.redux.js.org/docs/advanced/Middleware.html
 * @param store
 */
const logger = store => next => action => {
    if (typeof action === 'function') {
        console.log('dispatching a function');
    } else {
        console.log('dispatching ', action);
    }
    const result = next(action);
    console.log('nextState ', store.getState());
    return result;
};

const middlewares = [
    middleware,
    logger,
    thunk,
];

/**
 * 創(chuàng)建store
 */
export default createStore(reducers, applyMiddleware(...middlewares));

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末行拢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子诞吱,更是在濱河造成了極大的恐慌舟奠,老刑警劉巖竭缝,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異沼瘫,居然都是意外死亡抬纸,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)耿戚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)湿故,“玉大人,你說(shuō)我怎么就攤上這事膜蛔√持恚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵皂股,是天一觀的道長(zhǎng)墅茉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)呜呐,這世上最難降的妖魔是什么就斤? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蘑辑,結(jié)果婚禮上洋机,老公的妹妹穿的比我還像新娘。我一直安慰自己以躯,他們只是感情好槐秧,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布啄踊。 她就那樣靜靜地躺著忧设,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颠通。 梳的紋絲不亂的頭發(fā)上址晕,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音顿锰,去河邊找鬼谨垃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛硼控,可吹牛的內(nèi)容都是我干的刘陶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼牢撼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼匙隔!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起熏版,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤纷责,失蹤者是張志新(化名)和其女友劉穎捍掺,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體再膳,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挺勿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了喂柒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片不瓶。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖胳喷,靈堂內(nèi)的尸體忽然破棺而出湃番,到底是詐尸還是另有隱情,我是刑警寧澤吭露,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布吠撮,位于F島的核電站,受9級(jí)特大地震影響讲竿,放射性物質(zhì)發(fā)生泄漏泥兰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一题禀、第九天 我趴在偏房一處隱蔽的房頂上張望鞋诗。 院中可真熱鬧,春花似錦迈嘹、人聲如沸削彬。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)融痛。三九已至,卻和暖如春神僵,著一層夾襖步出監(jiān)牢的瞬間雁刷,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工保礼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沛励,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓炮障,卻偏偏與公主長(zhǎng)得像目派,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胁赢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355