- Notifications
You must be signed in to change notification settings - Fork21
This is a tutorial / example of how to convert from a single screen based app to a tab based app using React-Native-Navigation By Wix and Redux. This is useful for apps that want to have an login / authentication screens that transition after a login.
keri4141/React-Native-Navigation-Redux-Example
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
$ npm install
$ react-native run-ios
$ react-native run-android
React Native version: 0.47.1
/src --/actions actiontypes.js index.js --/components --/screens homeTab.js login.js screens.js searchTab.js --/img checkmark.png --/reducers index.js rootReducer.js app.js
The app component will behave as our overall application, and within app the navigators will exist in there
app SingleScreenApp Login TabScreenApp home search
The functionstartApp() will change the app's current navigator based on our variableroot the values thatroot can have arelogin (SingleScreenApp) for when the user is logging in for the first time andafter-login (TabScreenApp) for when the user has logged in.
In order for the app to know whether or not these live changes happened we must use Redux's Store. Store has three important methods,getState(), it gets the current state of the redux store, the second Store method is calleddispatch(), it lets you dispatch actions to let you change the state of your application using reducers. With the help of the third redux store method, calledsubscribe(), it lets you register a callback that redux will call anytime an action has been dispatched
By subscribing our store to the methodonStoreUpdate(), whenever an action is dispatched,onStoreUpdate() will be ran to check if the root has been altered. If it has, it will runstartApp() with the new root value:login, orafter-login.
export default class App extends Component { constructor(props) { super(props); store.subscribe(this.onStoreUpdate.bind(this)); store.dispatch(appActions.appInitialized()); } onStoreUpdate() { let {root} = store.getState().app; // handle a root change if (this.currentRoot != root) { this.currentRoot = root; this.startApp(root); } } startApp(root) { switch (root) { Navigation.startSingleScreenApp({ case 'login': screen: { screen: 'ReactNativeReduxExample.Login', title: 'Welcome', navigatorStyle: {}, navigatorButtons: {} }, }); return; case 'after-login': Navigation.startTabBasedApp({ tabs: [ { label: 'Home', screen: 'ReactNativeReduxExample.HomeTab', icon: require('./img/checkmark.png'), selectedIcon: require('./img/checkmark.png'), title: 'Hey', overrideBackPress: false, //this can be set to true for android navigatorStyle: {} }, { label: 'Search', screen: 'ReactNativeReduxExample.SearchTab', icon: require('./img/checkmark.png'), selectedIcon: require('./img/checkmark.png'), title: 'Hey', navigatorStyle: {} } ], }); return; default: //no root found } }
To have your components access to the states that you are keeping track of through redux, you pass the store and provider when registering your navigation components.
import { Navigation } from 'react-native-navigation';import Login from './login';import HomeTab from './homeTab';import SearchTab from './searchTab';export default (store, Provider) => {Navigation.registerComponent('ReactNativeReduxExample.Login', () => Login, store, Provider);Navigation.registerComponent('ReactNativeReduxExample.HomeTab', () => HomeTab, store, Provider);Navigation.registerComponent('ReactNativeReduxExample.SearchTab', () => SearchTab, store, Provider);}
In Redux, actions are simply objects that describe the type of changes being done in the app, and reducers are functions that perform those changes directly to the state.
ThecombineReducers() creates a mapping of which reducer will handle which state field in our Store. As an example:
the key,todoslist is a field in our state object, and the value next to that key represents the reducer that will handle that field in our state object. The same idea is applied to thevisibilityFilter key in this example. By convention, you should name the the reducer the same as the state that it is handling.
const todoApp= combineReducers({ todoslist: todoslist, visibilityFilter: visibilityFilter});
Since the key and value are the same, we can use ES6 shorthand notation to get the same results.
const todoApp= combineReducers({ todoslist, visibilityFilter});
Keeping this idea in mind, we head back to our own code. In our application we have the root reducer handling the root state. In redux, state must not be mutable.
The file../actions/actiontypes will just hold constants to indicate what type of action is being done.
import * as types from '../actions/actiontypes';import Immutable from 'seamless-immutable';const initialState = Immutable({ root: undefined // 'login' / 'after-login'});//root reducerexport default function root(state = initialState, action = {}) { switch (action.type) { case types.ROOT_CHANGED: return state.merge({ root: action.root }); default: return state; }}
We then bundle our reducers intoReducers/index.js:
import root from './appReducer';export { root}
So that we can use this object that is being exported to be placed into our combineReducers method
import * as reducers from "./reducers/index";const reducer = combineReducers(reducers);
We now know know which reducer handles which state field in our application.
We define an action creator, which is just a function that returns an action object. An action object should always have thetype key field, to indicate what action is being done.
We then createdappInitialized() method to set the root value as login for when the user first opens the application,appInitialized() calls thechangeAppRoot(), which will return a a new action and dispatch that action to our reducers. The same logic is applied tologin()
Thanks to theredux thunk we can make asynchronous requests based on our actions, vanilla redux is not capable of doing this. We would want this capability because for example, if a user clicked a button to do some API request, that event should grab data from a server, and then when that request is resolved, we create our action.
for a clearer explanation of redux thunk refer to References section below
return async function(dispatch, getState) { // since all business logic should be inside redux actions // this is a good place to put your app initialization code //run action creator to return action
import * as types from './actiontypes';//action creatorexport function changeAppRoot(root) { return { type: types.ROOT_CHANGED, root: root };}export function appInitialized() { return async function(dispatch, getState) { // since all business logic should be inside redux actions // this is a good place to put your app initialization code dispatch(changeAppRoot('login')); };}export function login() { return async function(dispatch, getState) { // login logic would go here, and when it's done, we switch app roots dispatch(changeAppRoot('after-login')); };}
Now that we have our actions defined, we created a method called onLoginPress, which will dispatch the actionlogin().
We then must make this component be literally connected to redux's store so that we can call dispatch. We do this by running:export default connect()(Login)
import {connect} from 'react-redux';import * as appActions from '../../actions/index';..........<Button large onPress={this.onLoginPress.bind(this)} title="Continue">....... onLoginPress() { this.props.dispatch(appActions.login()); }.......export default connect()(Login);
Redux is now set up with your wix navigator and a component which handles the navigation state.
React-Native-Navigation Wixhttps://wix.github.io/react-native-navigation/#/
Reduxhttps://egghead.io/lessons/javascript-redux-the-single-immutable-state-tree
Redux thunkhttps://www.youtube.com/watch?v=1QI-UE3-0PU
On Android, when a user presses the hardware's backbutton, the application is minimized but when it is re-opened, it behaves as if it is launching for the first time, bringing you back to the single screen view. This behavior does not occur when the user presses the hardware's home button or 'the right button on the android'.
To fix this, you could save a Javascript Web Token (JWT) using AsyncStorage once the user is done "signing up" and if the user closes the app and re-opens it, the application should check if there is a JWT that exists on the user's phone. The JWT data will persist thanks to AsyncStorage.
About
This is a tutorial / example of how to convert from a single screen based app to a tab based app using React-Native-Navigation By Wix and Redux. This is useful for apps that want to have an login / authentication screens that transition after a login.
Topics
Resources
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.