RN development generally combines some plug-in libraries for processing data streams, such as REdux, MOBx, DVA, etc. Dva is based on Redux and Redux-Saga, with built-in React-Router and FETCH. Mobx is easy to use, flexible and easy to use; Redux, on the other hand, is something that a lot of novices think is not only a bit more code to write, but also a bit more cumbersome to integrate. In fact, it can be encapsulated or used improperly, resulting in abuse of Redux and making it look cumbersome. This post introduces the use of React-Navigation and Redux in RN from an application perspective.
RN mainly has two routing libraries, react-navigation and react-native router-flux. The latter is also encapsulated based on the former, but it is easier to use. The documentation is definitely not as specific as react-navigation. So I recommend starting with React-Navigation.
Note the react-Navigation and redux versions. There may be minor differences between different versions
Package. json introduces the following libraries:
"React-native gesture-handler": "^1.0.12", "react-native reanimated": "^1.13.2", "react-navigation": "^4.0.1", "react navigation-redux-helpers", "react navigation-redux-helpers", "react navigation-stack": "^ 2.10.2 react - navigation -", "tabs" : "^ 2.10.1", "the react - redux" : "5.1.1", "story" : "^ 4.0.1", "the story - the logger" : "^ 3.0.6 redux -", "thunk" : "^ 2.3.0." "Copy the code
First, the react – navigation
1. Create bottom Tab
- createBottomTabNavigator
There are two bottom tabs, as shown below:
tab1.png tab2.png
The code to implement Tab1 is as follows:
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { createDrawerNavigator } from 'react-navigation-drawer';
const TabNavigator = createBottomTabNavigator({
Home: {
screen: HomeScreen,
},
Goods: {
screen: GoodsScreen,
},
Message: {
screen: MessageScreen,
},
Mine: {
screen: MineScreen,
}
}, {
defaultNavigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, horizontal, tintColor }) => {
const { routeName } = navigation.state;
let icon;
if (routeName === 'Home') {
icon = focused ? Images.tab.home_sel : Images.tab.home
} else if (routeName === 'Goods') {
icon = focused ? Images.tab.goods_sel : Images.tab.goods
} else if (routeName === 'Message') {
icon = focused ? Images.tab.message_sel : Images.tab.message
} else if (routeName === 'Mine') {
icon = focused ? Images.tab.mine_sel : Images.tab.mine
}
return <Image source={icon} style={{ width: 24, height: 24 }} />
},
}),
tabBarOptions: {
initialRouteName: 'Home',
activeTintColor: colors.activeTintColor,
inactiveTintColor: colors.inactiveTintColor,
}
});
const AppNavigator = createStackNavigator({
Main: {
screen: TabNavigator,
},
Login: {
screen: Login
},
}, {
mode: 'modal',
headerMode: 'none',
});
export default AppContainer = createAppContainer(AppNavigator);
Copy the code
Tab2 requires a custom tabBarComponent, which looks like this:
const TabNavigator = createBottomTabNavigator({ Home: { screen: HomeScreen, }, Goods: { screen: GoodsScreen, }, Message: { screen: MessageScreen, }, Mine: { screen: MineScreen, } }, { tabBarComponent: (props) => ( <MyCustomTaBar {... props} /> ) });Copy the code
- The implementation of MyCustomTaBar is also very simple, let the UI design a special background, the code is as follows:
import React, { Component } from 'react'; import { View, Text, ImageBackground, Image, StyleSheet } from 'react-native'; import { TouchableOpacity, TouchableWithoutFeedback } from 'react-native-gesture-handler'; import { colors } from '.. /.. /common/theme/color'; import { Images } from '.. /.. /image'; export default class MyCustomTaBar extends Component { render() { // console.log(JSON.stringify(this.props)); const { state } = this.props.navigation state.routes.forEach((e, index) => { if (state.index == index) { e.focused = true } else { e.focused = false } }); return ( <View> <ImageBackground style={{ width: SCREEN_WIDTH, height: 56, backgroundColor: 'transparent' }} source={Images.tab.foot}> <View style={{ flex: 1, flexDirection: 'row', backgroundColor: 'transparent' }}> { state.routes.length > 0 && state.routes.map((item, index) => { return <Item {... this.props} key={index} routeName={item.routeName} focused={item.focused} /> }) } </View> </ImageBackground> </View> ); } } const Item = class extends Component { getIcon = () => { const { routeName, focused } = this.props; let icon; if (routeName === 'Home') { icon = focused ? Images.tab.home_sel : Images.tab.home } else if (routeName === 'Goods') { icon = focused ? Images.tab.goods_sel : Images.tab.goods } else if (routeName === 'Message') { icon = focused ? Images.tab.message_sel : Images.tab.message } else if (routeName === 'Mine') { icon = focused ? Images.tab.mine_sel : Images.tab.mine } return icon } getName = () => { const { routeName, focused } = this.props; let name; If (routeName === 'Home') {name = 'Home'} else if (routeName === 'Goods') {name = 'Goods'} else if (routeName === 'Goods') {name = 'Goods'} else if (routeName === 'Goods') {name = 'Goods'} else if (routeName === 'Goods' 'Message') {name = 'Message'} else if (routeName === 'Mine') {name = 'my'} return name} gotoRoute = (routeName) => { this.props.navigation.navigate(routeName) } render() { const { routeName, focused } = this.props; if (routeName == 'Goods') { return (<TouchableWithoutFeedback onPress={() => { this.gotoRoute(routeName) }} style={{ // flex: 1, height: 100, width: SCREEN_WIDTH / 3, justifyContent: 'center', alignItems: 'center', top: -30, backgroundColor: 'transparent' }}> <View style={{ bottom: 10, }}> <View style={{ width: 50, height: 50, borderRadius: 25, backgroundColor: colors.theme }}></View> {/* <Image source={this.getIcon()} style={{ width: 40, height: 40 }} /> */} </View> <View> <Text style={focused ? styles.activeTintColor : styles.inactiveTintColor}>{this.getName()}</Text> </View> </TouchableWithoutFeedback>) } return ( <TouchableOpacity onPress={() => { this.gotoRoute(routeName) }} style={{ flex: 1, width: SCREEN_WIDTH / 3, justifyContent: 'center', alignItems: 'center', }}> <Image source={this.getIcon()} style={{ width: 20, height: 20 }} /> <Text style={focused ? styles.activeTintColor : styles.inactiveTintColor}>{this.getName()}</Text> </TouchableOpacity> ) } } const styles = StyleSheet.create({ activeTintColor: { color: colors.activeTintColor, fontSize: 12, }, inactiveTintColor: { color: colors.inactiveTintColor, fontSize: 12 } });Copy the code
2. Create drawers
- createDrawerNavigator
The use of drawer components is nothing more than opening and closing:
this.props.navigation.openDrawer()
this.props.navigation.closeDrawer()
The integration code is shown below:
const DrawerNavigator = createDrawerNavigator({ Main: { screen: AppNavigator, }, drawerA: { screen: DrawerScreen }, drawerB: { screen: DrawerBScreen }, }, { order: ['Main', 'drawerA', 'drawerB'],// Define the order of drawer items initialRouteName: 'Main', drawerType: 'front', drawerLockMode: 'unlocked',// whether to respond with gesture drawerWidth: 250, // the drawerWidth drawerPosition: 'left', // the option is left or right useNativeAnimations: Theme, // drawerBackgroundColor: colors. Theme, // drawerBackgroundColor contentComponent: (props) => (<DrawerBScreen {... props} />) }); export default AppContainer = createAppContainer(DrawerNavigator);Copy the code
The DrawerBScreen component is your custom page. One drawback is that the drawer component of the react-Navigation does not support gesture return, so react-native drawer layout is recommended.
The purpose of this article is not to introduce the use of each API, but to illustrate some common application scenarios. It is recommended to review the official documentation for beginners.
Second, the story
Redux has three principles:
- Single data source: The state of the entire application is consolidated in one store
- State is read-only: the State can only be changed by issuing actions
- Use pure functions to perform changes: The Reducer is just some pure functions that receive the previous state and action and return the new state.
The official example code can be seen here: the redux sample code, which only shows redux in RN.
To integrate Redux into RN, do the following:
- CreateStore – Creates a store
import { createStore, applyMiddleware } from 'redux'; import thunk from "redux-thunk" import { createReactNavigationReduxMiddleware, } from 'react-navigation-redux-helpers'; import appReducer from './reducers/index' const middleware = createReactNavigationReduxMiddleware( state => state.nav, 'root' ); const middlewares = [ middleware, thunk ] const store = createStore( appReducer, applyMiddleware(... middlewares), ); export default storeCopy the code
- Encapsulate appReducer, and all reducer are uniformly placed here
import { combineReducers } from 'redux';
import {navReducer} from './navReducer'
import {login} from './loginReducer'
const appReducer = combineReducers({
nav:navReducer,
login:login
});
export default appReducer
Copy the code
- Create a navReducer to save routing data
import { createNavigationReducer, } from 'react-navigation-redux-helpers'; import AppContainer from '.. /.. Export const navReducer = createNavigationReducer(AppContainer); /router/index' // Export const navReducer = createNavigationReducer(AppContainer);Copy the code
- Take loginReducer as an example to illustrate the whole reducer process:
1. Create new actionTypes
Action export const LOGINING = 'LOGINING' export const LOGIN_SUCCESS = 'LOGIN_SUCCESS' export const LOGIN_ERROR = 'LOGIN_ERROR' export const LOGOUT = 'LOGOUT'Copy the code
2. Create an action function to distribute
import * as actionType from '.. /actionsTypes/index' import { LoginInfo } from '.. /.. /redux/reducers/loginReducer' export function login(name, psw) { // console.log(name, psw); Return dispatch => {dispatch(logining()) fetch('https://www.baidu.com/', 'get') .then(res => { dispatch(loginSuccess({ name, psw })) }) .catch(e => { dispatch(loginFail()) }) } } export function logining() { return { type: actionType.LOGINING } } export function loginSuccess(userInfo) { return { type: actionType.LOGIN_SUCCESS, state: userInfo } } export function loginFail() { return { type: actionType.LOGIN_ERROR } } export function loginOut() { return { type: actionType.LOGOUT } }Copy the code
Reducer change state and return
import * as type from '.. /actionsTypes/index' export const LoginInfo = {status: "Not logged in ", isLogin: false, user: {},}; export const login = function (state = LoginInfo, action) { switch (action.type) { case type.LOGINING: return { ... State, status: "login ", isLogin: false,}; case type.LOGIN_SUCCESS: return { ... State, status: "login succeeded ", isLogin: true, user: action.state}; case type.LOGIN_ERROR: return { ... State, status: "Login failed ", isLogin: false, user: {}}; case type.LOGOUT: return { ... State, status: "Not logged in ", isLogin: false, user: {}}; default: return state; }}Copy the code
4. Finally, visit reducer with connect in your page
const mapStateToProps = (state) => ({
nav: state.nav,
status: state.login.status,
user: state.login.user
})
const mapDispatchToProps = dispatch => ({
login: (name, psd) => dispatch(actions.login(name, psd)),
loginOut: () => dispatch(actions.loginOut())
});
export default connect(mapStateToProps, mapDispatchToProps)(Login)
Copy the code
If you’re using VSCode to develop react, hitting rcredux when creating a react component will quickly create a react component that contains redux. Of course, there are many similar generated code, vue also has. Similar. Vue file, input vbase to quickly generate template code.
React-navigation and Redux are compatible
With this in mind, react-Navigation and Redux are much easier to implement.
import React, { Component } from 'react'; import { StatusBar, BackHandler, ToastAndroid } from 'react-native'; import { Provider, connect } from 'react-redux' import store from './redux/index' import AppContainer from './router/index' import { NavigationActions } from 'react-navigation'; import {createReduxContainer} from 'react-navigation-redux-helpers' const AppWithRedux=createReduxContainer(AppContainer,'root') const mapStateToProps = (state) => ({ state: state.nav, }); const AppWithNavigationState = connect(mapStateToProps)(AppWithRedux) export default class App extends Component { constructor(props) { super(props) this.lastBackPressed = null } componentDidMount() { BackHandler.addEventListener("hardwareBackPress", this.onBackPress); } componentWillUnmount() { BackHandler.removeEventListener("hardwareBackPress", this.onBackPress); } onBackPress = () => { // alert(JSON.stringify(store.getState())) if (store.getState().nav.index ! == 0) { store.dispatch(NavigationActions.back()); If (this.lastBackPressed && this.lastbackPressed + 2000 >= date.now ()) {// The back key has been pressed in the last 2 seconds to exit the application. return false; } this.lastBackPressed = Date.now(); Toastandroid. show(' Press to exit app again ', toastAndroid.short); return true; }; render() { return ( <Provider store={store}> <AppWithNavigationState /> </Provider> ) } }Copy the code
- A Provider component is provided, and the outermost layer is passed into a Store, so that all the following child components can receive the reducer state, which is the React context
- Note that the key–‘root’ — is the same as the store key:
const AppWithRedux=createReduxContainer(AppContainer,'root')
Copy the code
const middleware = createReactNavigationReduxMiddleware(
state => state.nav,
'root'
);
Copy the code
- Android also writes a physical back listener that controls whether to exit the app and return to the page
If you still don’t like the hassle of Redux, mobx is a better option. For mobX configuration, see the following demo: React-Navigation integrates MOBX