为了帮助大家快速上手在React Native与Redux开发,在这本文中将向大家介绍如何在React Native中使用Redux和react-navigation组合?,以及一些必备基础以及高级知识。
本参考了《新版React Native+Redux打造高质量上线App》课程的部分讲解,更多关于React Native与Redux的实战技巧可在《新版React Native+Redux打造高质量上线App》中查看。
那么如何在React Native中使用Redux和react-navigation组合?呢?
在使用 React Navigation 的项目中,想要集成 redux 就必须要引入 react-navigation-redux-helpers
这个库。
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之前执行否则会报错,以上代码片段的完整部分可以在课程源码中查找。
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 | Reducer}
*/
const index = combineReducers({
nav: navReducer,
theme: theme,
});
export default index;
以上代码片段的完整部分可以在课程源码中查找。
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));
以上代码片段的完整部分可以在课程源码中查找。
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>
}
}
以上代码片段的完整部分可以在课程源码中查找。
经过上述4步呢,我们已经完成了react-navigaton+redux的集成,那么如何使用它呢?
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
}
}
以上代码片段的完整部分可以在课程源码中查找。
这样你就能轻松的跳回到这个对象之前的某个状态(想象一个撤销功能)。