爲了幫助你們快速上手在React Native與Redux開發,在這本文中將向你們介紹如何在React Native中使用Redux和react-navigation組合?,以及一些必備基礎以及高級知識。html
本參考了《新版React Native+Redux打造高質量上線App》課程的部分講解,更多關於React Native與Redux的實戰技巧可在《新版React Native+Redux打造高質量上線App》中查看。react
那麼如何在React Native中使用Redux和react-navigation組合?呢?android
在使用 React Navigation 的項目中,想要集成 redux 就必需要引入 react-navigation-redux-helpers
這個庫。git
npm install --save react-navigation-redux-helpers
複製代碼
import React from 'react';
import {createStackNavigator, createSwitchNavigator} from 'react-navigation';
import {connect} from 'react-redux';
import {createReactNavigationReduxMiddleware, reduxifyNavigator} from 'react-navigation-redux-helpers';
export const rootCom = 'Init';//設置根路由
export const RootNavigator = createSwitchNavigator({
...
});
/** * 1.初始化react-navigation與redux的中間件, * 該方法的一個很大的做用就是爲reduxifyNavigator的key設置actionSubscribers(行爲訂閱者) * 設置訂閱者@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L29 * 檢測訂閱者是否存在@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.將根導航器組件傳遞給 reduxifyNavigator 函數, * 並返回一個將navigation state 和 dispatch 函數做爲 props的新組件; * 注意:要在createReactNavigationReduxMiddleware以後執行 */
const AppWithNavigationState = reduxifyNavigator(RootNavigator, 'root');
/** * State到Props的映射關係 * @param state */
const mapStateToProps = state => ({
state: state.nav,//v2
});
/** * 3.鏈接 React 組件與 Redux store */
export default connect(mapStateToProps)(AppWithNavigationState);
複製代碼
提示:createReactNavigationReduxMiddleware方法要放到reduxifyNavigator以前執行不然會報錯,以上代碼片斷的完整部分能夠在課程源碼中查找。github
import {combineReducers} from 'redux'
import theme from './theme'
import {rootCom, RootNavigator} from '../navigator/AppNavigators';
//1.指定默認state
const navState = RootNavigator.router.getStateForAction(RootNavigator.router.getActionForPathAndParams(rootCom));
/** * 2.建立本身的 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,
});
export default index;
複製代碼
以上代碼片斷的完整部分能夠在課程源碼中查找。npm
import {applyMiddleware, createStore} from 'redux'
import thunk from 'redux-thunk'
import reducers from '../reducer'
import {middleware} from '../navigator/AppNavigators'
const middlewares = [
middleware,
];
/** * 建立store */
export default createStore(reducers, applyMiddleware(...middlewares));
複製代碼
以上代碼片斷的完整部分能夠在課程源碼中查找。redux
import React, {Component} from 'react';
import {Provider} from 'react-redux';
import AppNavigator from './navigator/AppNavigators';
import store from './store'
type Props = {};
export default class App extends Component<Props> {
render() {
/** * 將store傳遞給App框架 */
return <Provider store={store}> <AppNavigator/> </Provider>
}
}
複製代碼
以上代碼片斷的完整部分能夠在課程源碼中查找。react-native
通過上述4步呢,咱們已經完成了react-navigaton+redux的集成,那麼如何使用它呢?bash
1.訂閱state服務器
import React from 'react';
import {connect} from 'react-redux';
class TabBarComponent extends React.Component {
render() {
return (
<BottomTabBar {...this.props} activeTintColor={this.props.theme} /> ); } } const mapStateToProps = state => ({ theme: state.theme.theme, }); export default connect(mapStateToProps)(TabBarComponent); 複製代碼
以上代碼片斷的完整部分能夠在課程源碼中查找。
在上述代碼中咱們訂閱了store中的theme state,而後該組件就能夠經過this.props.theme
獲取到所訂閱的theme state了。
2.觸發action改變state
import React, {Component} from 'react';
import {connect} from 'react-redux'
import {onThemeChange} from '../action/theme'
import {StyleSheet, Text, View, Button} from 'react-native';
type Props = {};
class FavoritePage extends Component<Props> {
render() {
return (
<View style={styles.container}> <Text style={styles.welcome}>FavoritePage</Text> <Button title="改變主題色" onPress={() => { // let {dispatch} = this.props.navigation; // dispatch(onThemeChange('red')) this.props.onThemeChange('#096'); }} /> </View> ); } } const mapStateToProps = state => ({}); const mapDispatchToProps = dispatch => ({ onThemeChange: (theme) => dispatch(onThemeChange(theme)), }); export default connect(mapStateToProps, mapDispatchToProps)(FavoritePage); ... 複製代碼
以上代碼片斷的完整部分能夠在課程源碼中查找。
觸發action有兩種方式:
一種是經過mapDispatchToProps將dispatch建立函數和props綁定,這樣就能夠經過this.props.onThemeChange('#096')
調用這個dispatch建立函數來觸發onThemeChange
action了;
另一種方式是經過this.props中的navigation來獲取dispatch,而後經過這個dispatch手動觸發一個action:
let {dispatch} = this.props.navigation;
dispatch(onThemeChange('red'))
複製代碼
以上代碼片斷的完整部分能夠在課程源碼中查找。
在Redux+react-navigation場景中處理Android的物理返回鍵須要注意當前路由的因此位置,而後根據指定路由的索引位置來進行操做,這裏須要用到BackHandler。
import React, {Component} from 'react';
import {BackHandler} from "react-native";
import {NavigationActions} from "react-navigation";
import {connect} from 'react-redux';
import DynamicTabNavigator from '../navigator/DynamicTabNavigator';
import NavigatorUtil from '../navigator/NavigatorUtil';
type Props = {};
class HomePage extends Component<Props> {
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress", this.onBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.onBackPress);
}
/** * 處理 Android 中的物理返回鍵 * https://reactnavigation.org/docs/en/redux-integration.html#handling-the-hardware-back-button-in-android * @returns {boolean} */
onBackPress = () => {
const {dispatch, nav} = this.props;
//if (nav.index === 0) {
if (nav.routes[1].index === 0) {//若是RootNavigator中的MainNavigator的index爲0,則不處理返回事件
return false;
}
dispatch(NavigationActions.back());
return true;
};
render() {
return <DynamicTabNavigator/>;
}
}
const mapStateToProps = state => ({
nav: state.nav,
});
export default connect(mapStateToProps)(HomePage);
複製代碼
以上代碼片斷的完整部分能夠在課程源碼中查找。
2end
隨着應用變得愈來愈複雜,能夠考慮將 reducer 函數 拆分紅多個單獨的函數,拆分後的每一個函數負責獨立管理 state 的一部分。
函數原型:
combineReducers(reducers)
參數:reducers (Object): 一個對象,它的值(value)對應不一樣的 reducer 函數,這些 reducer 函數後面會被合併成一個。下面會介紹傳入 reducer 函數須要知足的規則。
每一個傳入 combineReducers 的 reducer 都需知足如下規則:
返回值
(Function):一個調用 reducers 對象裏全部 reducer 的 reducer,而且構造一個與 reducers 對象結構相同的 state 對象。
combineReducers
輔助函數的做用是,把一個由多個不一樣 reducer 函數做爲 value 的 object,合併成一個最終的 reducer 函數,而後就能夠對這個 reducer 調用 createStore 方法。
合併後的 reducer 能夠調用各個子 reducer,並把它們返回的結果合併成一個 state 對象。 由 combineReducers()
返回的 state 對象,會將傳入的每一個 reducer 返回的 state 按其傳遞給 combineReducers()
時對應的 key 進行命名。
提示:在 reducer 層級的任何一級均可以調用 combineReducers。並非必定要在最外層。實際上,你能夠把一些複雜的子 reducer 拆分紅單獨的孫子級 reducer,甚至更多層。
函數原型:
createStore(reducer, [preloadedState], enhancer)
參數
reducer (Function):
:項目的根reducer。[preloadedState] (any)
:這個參數是可選的, 用於設置 state 初始狀態。這對開發同構應用時很是有用,服務器端 redux 應用的 state 結構能夠與客戶端保持一致, 那麼客戶端能夠將從網絡接收到的服務端 state 直接用於本地數據初始化。enhancer (Function)
: Store enhancer 是一個組合 store creator 的高階函數,返回一個新的強化過的 store creator。這與 middleware 類似,它也容許你經過複合函數改變 store 接口。返回值
(Store)
: 保存了應用全部 state 的對象。改變 state 的唯一方法是 dispatch action。你也能夠 subscribe 監聽 state 的變化,而後更新 UI。示例
import { createStore } from 'redux'
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([action.text])
default:
return state
}
}
let store = createStore(todos, ['Use Redux'])
store.dispatch({
type: 'ADD_TODO',
text: 'Read the docs'
})
console.log(store.getState())
// [ 'Use Redux', 'Read the docs' ]
複製代碼
以上代碼片斷的完整部分能夠在課程源碼中查找。
注意事項
函數原型:
applyMiddleware(...middleware)
使用包含自定義功能的 middleware 來擴展 Redux。
上述的實戰技巧可在新版React Native+Redux打造高質量上線App中獲取;
從不直接修改 state 是 Redux 的核心理念之一:爲實現這一理念,能夠經過一下兩種方式:
在下面的 todoApp 示例中, Object.assign() 將會返回一個新的 state 對象, 而其中的 visibilityFilter 屬性被更新了:
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
default:
return state
}
}
複製代碼
以上代碼片斷的完整部分能夠在課程源碼中查找。
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return { ...state, visibilityFilter: action.filter }
default:
return state
}
}
複製代碼
以上代碼片斷的完整部分能夠在課程源碼中查找。
這樣你就能輕鬆的跳回到這個對象以前的某個狀態(想象一個撤銷功能)。