React Navigation 集成 Redux

更新时间:2018-03-03

修复因 React Navigation 更新引起的 addListener is not a function 的问题。


react-navigation 是 React Native 官方推荐的导航库。redux 是一个状态容器,redux 的简单使用可参考阮一峰的 Redux 入门教程,现在网上也有很多中文文档。

在 React Navigation 最新版本中需要添加 react-navigation-redux-helpers

$ yarn add react-native-navigation-redux-helpers # 或者 npm install --save react-native-navigation-redux-helpers

首先写 redux helper 工具 redux.js

import { createReactNavigationReduxMiddleware, createReduxBoundAddListener } from 'react-navigation-redux-helpers'; // 注意: createReactNavigationReduxMiddleware 必须在 createReduxBoundAddListener 之前执行 const middleware = createReactNavigationReduxMiddleware('root', state => state.nav,); const addListener = createReduxBoundAddListener('root'); export { middleware, addListener };

首先写一个 navigator

export const AppNavigator = StackNavigator({ Home: { screen: HomeTab }, // 这是一个 TabNavigator Details: { screen: DetailPage }, // 简单的页面 });

集成 Redux 主要含三部分:Store,Action,Reducer

Redux

Action

export function getBanners() { return dispatch => { // request 是我自己封装的网络请求方法 request({ url: apis.BANNER // 常量 API 地址 }).then(res => { dispatch({ type: actionTypes.BANNER, // 常量行为类型 data: res }) }) } }

Reducer

  1. othersReducer.js
// 初始 state const initState = { banners: [], articles: [], loading: false } export default function discoveryData(state=initState, action) { switch(action.type) { case actionTypes.BANNER: return {...state, banners:action.data, loading:false} default: return state; } }
  1. navReducer.js 用于导航
import { AppNavigator } from '../navigators/AppNavigator'; import { NavigationActions } from 'react-navigation'; // Home 是个 TabNavigator,在这里 firstAction 为 null,会导致后续程序会出错,所以自己手写了一个 action const firstAction = AppNavigator.router.getActionForPathAndParams('Home') || { type: 'Navigation/NAVIGATE', touteName: 'Home' }; const initialNavState = AppNavigator.router.getStateForAction(firstAction); const navReducer = (state = initialNavState, action) => { const nextState = AppNavigator.router.getStateForAction(action, state); return nextState || state; } export default navReducer;

在以前版本里,我是把 firstAction 和 initialNavState 注释掉了,如果不注释掉会在 getStateForAction 时报 undefined 错误 undefined 解决方案多种: 一种是将 TabNavigator 放在单纯的 Component 中作为 StackNavigator 的 screen,这样就可以使用 initialNavState了,弊端是不方便从其它页面跳转到 TabNavigator 的指定 Tab 页。 另一种就是我现在使用的方案了。 如果你有更好的解决方案请告诉我!!

在新版本中不再出现上述问题,而是出现代码中提到的 null 问题 3. 整合 Reducer

import { combineReducers } from 'redux'; import navReducer from './navReducer'; import othersReducer from './othersReducer'; const AppReducer = combineReducers({ nav: navReducer, othersReducer }); export default AppReducer;

配置 Store

import { createStore, applyMiddleware } from 'redux'; import thunkMiddleware from 'redux-thunk'; // 中间件 import reducers from './../reducers'; // 整合后的 Reducer import { middleware } from './redux'; // redux helper const configStore = applyMiddleware(thunkMiddleware, middleware)(createStore) export default configStore(reducers);

自定义 Navigator

import { connect } from 'react-redux'; import { addNavigationHelpers, NavigationActions } from 'react-navigation'; import { addListener } from './redux'; class AppWithNavigationState extends Component { componentDidMount() { BackHandler.addEventListener('hardwareBackPress', this.onBackPress) } componentWillUnmount() { BackHandler.removeEventListener('hardwareBackPress', this.onBackPress) } onBackPress = () => { const { dispatch, nav } = this.props; dispatch(NavigationActions.back()) return !(nav.index===0) } render() { return ( <AppNavigator navigation={addNavigationHelpers({ dispatch: this.props.dispatch, state: this.props.nav, addListener // 关键所在 })} /> ) } } const mapStateToProps = state => ({ nav: state.nav, }); export default connect(mapStateToProps)(AppWithNavigationState);

绑定到应用中

在入口文件中绑定 Redux 的 store 管理库

import { Provider } from 'react-redux'; import store from './src/store'; export default class App extends React.Component { render() { return ( <Provider store={store}> <AppWithNavigationState /> </Provider> ); } }

所有源码我已经放到了 GitHub 查看源码