1. 程式人生 > >React-Navigation與Redux整合詳解

React-Navigation與Redux整合詳解

繼react-navigation釋出已經過去半年的時間,想必React Native的玩家早已玩轉於手掌了。如果你還不瞭解,就out啦~還在等什麼?

Redux框架給開發者帶來的優勢是顯而易見的。它好比Android中的MVP架構一樣,使得複雜的業務邏輯和檢視狀態變得簡單、清晰。如何將react-navigation和Redux結合到一起呢?本篇部落格就來嘮嘮。

在搞定Redux與react-navigation整合之前,我們有必要先了解下在沒有使用react-navigation導航下的Redux專案架構,先來看一個簡單的Redux專案目錄:

一般情況下,Redux專案中都會包含如下幾個目錄:

(1)Action

(2)Reducer

(3)Store

熟悉Redux的玩家肯定對這三個模組肯定都不陌生。Action中負責分發使用者行為;Reducer接收Action,並根據行為型別進行相應的邏輯處理,並返回最新狀態;Store中負責Action和Reducer的互動,並記錄Reducer處理的最新狀態。並且還可以應用中介軟體等;此時可利用react-redux將狀態與檢視繫結。這樣,Action -> Controller -> View就完美的形成了閉環流程。下面我們分別看下三者之間是如何銜接的。

1、Action

export let main = (url, params, isLoading, isLoadMore, isRefreshing)
=>
{ return dispatch => { // 1.發出拉取資料的訊號 dispatch(loadMainContent(isLoading, isLoadMore, isRefreshing)); // 2.請求網路 return HttpUtil.fetchGet(url, params, (responseObj) => { dispatch(receiveMainContent(responseObj.result.bookList)); console
.info("success"); }, (error) => { dispatch(receiveMainContent([])); console.info("error" + error); } ) } }
let loadMainContent = (isLoading, isLoadMore, isRefreshing) => {
    return {
        type: types.LOAD_MAIN_LIST,
        isLoading: isLoading,
        isLoadMore: isLoadMore,
        isRefreshing: isRefreshing
    }
}

let receiveMainContent = (mainList) => {
    return {
        type: types.GET_MAIN_LIST,
        mainList: mainList
    }
}

如上程式碼所示,在main Action中,我們定義了不同的Action行為,並通過dispatch進行分發。

2、Reducer

const initState = {
    mainList: [],
    isLoading: true,
    isLoadMore: false,
    isRefreshing: false
}

let mainReducer = (state = initState, action) => {
    switch (action.type) {
        case types.LOAD_MAIN_LIST:
            return Object.assign({}, state, {
                isLoading: action.isLoading,
                isLoadMore: action.isLoadMore,
                isRefreshing: action.isRefreshing
            });
        case types.GET_MAIN_LIST:
            return Object.assign({}, state, {
                isLoading: false,
                isRefreshing: false,
                mainList: state.isLoadMore ? state.mainList.concat(action.mainList) : action.mainList
            })
        default:
            return state;
    }
}

上面程式碼定義了對應的Reducer,用來接收Action的行為,並進行處理,最終返回最新狀態State。

3、整合Reducer

export default rootReducer = combineReducers({
    Main,

})

4、Store

let store = createStore(rootReducer, {}, compose(
  applyMiddleware(thunk),
  window.devToolsExtension ? window.devToolsExtension() : f => f
))

Store相對簡單,因為Redux本身沒有非同步概念,不能直接使用setTimeOut等,但是網路處理需要非同步進行,並且結果是非同步返回,所以為了處理此種情況,可以使用中介軟體react-thunk幫助我們完成。

5、Connect

import React, { Component } from 'react';
import { Provider } from 'react-redux';
import App from './components/app';
import store from './store/store';

export default class Root extends Component {

    render() {
        return (
            <Provider store={store}>
                <App />
            </Provider>
        )
    }
}

使用react-redux將store傳遞到Provider,從而將Redux和檢視層繫結在一起,完成Action -> Reducer ->  Store -> View的整體連線。

上面程式碼中App即檢視的入口,react-navigation其實就充當了程式的入口,負責檢視介面之間的跳轉互動。

const AppNavigator = StackNavigator(
    {
        Splash: { screen: SplashScene },
    }
)

在react-navigation中使用navigate來實現介面間跳轉行為。此時我們可以理解為Redux中的dispatch,即一個使用者行為。

1、dispatch的觸發需要Reducer的接收,所以我們需要定義react-navigation的Reducer:

import Routers from './Router';

const navReducer = (state,action) => {
    const newState = Routers.router.getStateForAction(action, state);
    return newState || state;
}

export default navReducer;

Router中就是我們定義的react-navigation的StackNavigator。navReducer即為接收跳轉狀態的Reducer,程式碼中我們通過獲取StackNavigator的Action來返回最新的處理狀態。

2、在rootReducer中註冊該reducer:

export default rootReducer = combineReducers({
    Nav
})

3、Store中仍然是註冊rootRecuder,使用中介軟體等等。

4、App首頁中繫結(app.js)

@connect(state => ({
    nav: state.nav
}))

class AppWithNavigationState extends Component {
    render() {
        return (
            <Router
                navigation={addNavigationHelpers({
                    dispatch: this.props.dispatch,
                    state: this.props.nav
                })}
            />
        );
    }
}

export default class App extends Component {

    render() {
        return(
            <Provider store={ store }>
                <AppWithNavigationState/>
            </Provider>

        )
    }
}

以上程式碼定義在程式入口中,從程式碼可以看到,首先使用@connect繫結nav的state,即NavReducer中返回的state。Router就是我們的StackNavigator,對其新增addNavigationHelpers,並將dispatch和state傳入。dispatch和nav就是Redux中分發的行為和狀態,這樣去觸發react-navigation的改變。最後使用Provider包含並將store注入到Provider。

總結下流程:

(1)定義navReducer,返回導航狀態。(跳轉State)

(2)註冊reducer。 (將navReducer新增到rootReducer)

(3)建立store。(store中注入rootReducer)

(4)程式入口中將store注入Provider。(Provider將store注入StackNavigator)

(5)@connect獲取最新導航狀態。(將StackNavigator於=與Redux繫結)

(6)設定StackNavigator的addNavigationHelpers,並將狀態和行為傳入。(StackNavigator接收到Reducer返回的最新狀態,執行相應改變(跳轉等))

以上步驟執行完,此時,程式會在@connect丟擲錯誤,因為我們使用了@描述符,所以需要引入如下第三方庫:

"babel-plugin-transform-decorators-legacy": "^1.3.4"

(1)npm i babel-plugin-transform-decorators-legacy --save -dev

(2)專案根目錄找到檔案,開啟新增如下:

{
  "presets": ["react-native"],
  "plugins":["transform-decorators-legacy"] // 新增引用外掛
}

ok,到此通過以上步驟,我們就將Redux和react-navigation整合在一起啦~

老規矩,原始碼奉上, 點選下載

剛建立的 React Native交流11群:112490774 ,歡迎各位大牛,React Native技術愛好者加入交流!同時部落格右側歡迎微信掃描關注訂閱號,移動技術乾貨,精彩文章技術推送!

尊重原創,轉載請註明:From Sky丶清(http://www.lcode.org) 侵權必究!