繼上次寫(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 的 dispatch
和 getState
方法。舉個(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:
- You can use redux-promise or redux-promise-middleware to dispatch Promises instead of functions.
- You can use redux-observable to dispatch Observables.
- You can use the redux-saga middleware to build more complex asynchronous actions.
- You can use the redux-pack middleware to dispatch promise-based asynchronous actions.
- You can even write a custom middleware to describe calls to your API, like the real world example does.
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)