I would like to use Redux for React Native. Currently I have no set data management, so the index.ios.js has the following:
renderScene(route, navigator){
this._navigator = navigator
return (
<route.component navigator={navigator} {...route.passProps}/>
)
}
<Navigator
configureScene={this.configureScene}
initialRoute={{name: 'Login', component: Login}}
renderScene={(route, navigator) => this.renderScene(route, navigator)}
style={styles.container}
navigationBar={
<Navigator.NavigationBar
style={styles.navBar}
routeMapper={NavigationBarRouteMapper}
/>
}
/>
I am planning to switch to Redux. With my current set up, not using any frameworks, only exception being react-router, what would be the most preferred way to navigate using Redux? A sample using store, reducers, and actions would be extremely helpful.
EDIT: react-navigation should be used now instead of NavigationExperimental.
--
I advise you use NavigationExperimental instead of Navigator. The last is depreciated. There is an example in the RN documentation, right here. It uses simple reducers to manage your navigation state. But you can also handle this state with Redux since it is the same logic.
This is my setup with tabs, using Redux and NavigationExperimental:
navigator.js (note: the renderOverlay prop in CardStack will be renamed renderHeader in 0.32)
import React from 'react';
import { NavigationExperimental } from 'react-native';
import { connect } from 'react-redux';
import { pop, push, selectTab } from './actions';
const {
CardStack: NavigationCardStack,
} = NavigationExperimental;
class Navigator extends React.Component {
constructor(props) {
super(props);
this._renderHeader = this._renderHeader.bind(this);
this._renderScene = this._renderScene.bind(this);
}
_renderHeader(sceneProps) {
return (
<MyHeader
{...sceneProps}
navigate={this.props.navigate}
/>
);
}
_renderScene(sceneProps) {
return (
<MyScene
{...sceneProps}
navigate={this.props.navigate}
/>
);
}
render() {
const { appNavigationState: { scenes, tabKey, tabs } } = this.props;
return (
<View style={{ flex: 1 }}>
<NavigationCardStack
key={`stack_${tabKey}`}
navigationState={scenes}
renderOverlay={this._renderHeader}
renderScene={this._renderScene}
/>
<Tabs
navigationState={tabs}
navigate={this.props.navigate}
/>
</View>
);
}
}
const getCurrentNavigation = (navigation) => {
const tabs = navigation.tabs;
const tabKey = tabs.routes[tabs.index].key;
const scenes = navigation[tabKey];
return {
scenes,
tabKey,
tabs,
};
};
const mapStateToProps = (state) => {
return {
appNavigationState: getCurrentNavigation(state.navigation),
};
};
const mapDispatchToProps = (dispatch) => {
return {
navigate: (action) => {
if (action.type === 'pop') {
dispatch(pop());
} else if (action.type === 'push' && action.route) {
dispatch(push(action.route));
} else if (action.type === 'tab' && action.tabKey) {
dispatch(selectTab(action.tabKey));
}
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(Navigator);
actions.js
export function pop() {
return ({
type: 'POP_ROUTE',
});
}
export function push(route) {
return ({
type: 'PUSH_ROUTE',
route,
});
}
export function selectTab(tabKey) {
return ({
type: 'SELECT_TAB',
tabKey,
});
}
reducer.js (note: I use immutable here, but it's up to you to use whatever fit you)
import { NavigationExperimental } from 'react-native';
import { Record } from 'immutable';
const {
StateUtils: NavigationStateUtils,
} = NavigationExperimental;
export const InitialState = Record({
// Tabs
tabs: {
index: 0,
routes: [
{ key: 'tab1' },
{ key: 'tab2' },
{ key: 'tab3' },
],
},
// Scenes
tab1: {
index: 0,
routes: [{ key: 'tab1' }],
},
tab2: {
index: 0,
routes: [{ key: 'tab2' }],
},
tab3: {
index: 0,
routes: [{ key: 'tab3' }],
},
});
const initialState = new InitialState();
export default function navigation(state = initialState, action) {
switch (action.type) {
case 'POP_ROUTE': {
const tabs = state.get('tabs');
const tabKey = tabs.routes[tabs.index].key;
const scenes = state.get(tabKey);
const nextScenes = NavigationStateUtils.pop(scenes);
if (scenes !== nextScenes) {
return state.set(tabKey, nextScenes);
}
return state;
}
case 'PUSH_ROUTE': {
const route = action.route;
const tabs = state.get('tabs');
const tabKey = tabs.routes[tabs.index].key;
const scenes = state.get(tabKey);
const nextScenes = NavigationStateUtils.push(scenes, route);
if (scenes !== nextScenes) {
return state.set(tabKey, nextScenes);
}
return state;
}
case 'SELECT_TAB': {
const tabKey = action.tabKey;
const tabs = state.get('tabs');
const nextTabs = NavigationStateUtils.jumpTo(tabs, tabKey);
if (nextTabs !== tabs) {
return state.set('tabs', nextTabs);
}
return state;
}
default:
return state;
}
}
Finally, in MyScene.js component you can simply handle the component to show by testing the current route (with a switch for example).
By passing the navigate function in your components, you can manage your scenes (ie: this.props.navigate({ type: 'push', route: { key: 'search' } })).
Notice that you can handle your state and your actions as you wish. The main idea to understand is how the state is reduced, regardless you use redux or not.
I suggest you use react-navigation for native and web environment it will replace NavigationExperimental just take a look the following example.
- Components built for iOS and Android
- Share navigation logic between mobile apps, web apps, and server rendering.
- Documentation is pretty straightforward
- Develop your own navigation views
- It is easy!
Just define your navigation in your app.
import {
TabNavigator,
} from 'react-navigation';
const BasicApp = TabNavigator({
Main: {screen: MainScreen},
Setup: {screen: SetupScreen},
});
...and navigate
class MainScreen extends React.Component {
static navigationOptions = {
label: 'Home',
};
render() {
const { navigate } = this.props.navigation;
return (
<Button
title="Go to Setup Tab"
onPress={() => navigate('Setup')}
/>
);
}
}
And do the following in order to add redux just define your reducer...
const AppNavigator = StackNavigator(AppRouteConfigs);
const appReducer = combineReducers({
nav: (state, action) => (
AppNavigator.router.getStateForAction(action, state)
)
});
...and navigate in your component with addNavigationHelpers
<AppNavigator navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav,
})} />