JSPM

  • Created
  • Published
  • Downloads 130
  • Score
    100M100P100Q57456F
  • License MIT

mobx navigation store for nested navigators using react navigation, including persist when needed.

Package Exports

  • mobx-react-navigation-store

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (mobx-react-navigation-store) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

mobx-react-navigation-store

mobx navigation store for nested navigators using react navigation, including persist when needed

Welcome

This project is still under construction and will be improved further if you want to contribute you can

  • see the Contributing section and then you can
    • open issues with ideas for improvments
    • help with documantion
    • PRs

of course you don't have to contribute to enjoy this package, so enjoy!

Importent

the docs are not up-to-date, they are correct for version ^0.2.0 , current version is 0.3.0 Added support for TabNavigators , including persist and nested navigators, currently tested only stacks nested inside tabs and not tabs in tabs or tabs in stacks

Why?

while working with react native I found that I need to use quite often with nested navigators and keeping the navigation state when the user closes the app. after many times implementing an app specific navigation store using mobx, so this project aspires to be a generic navigation state mangment store which supports navigation presisitence.

Working example

you can check out the very simple react native example app just click here and follow the instructions enjoy!

What's ready?

  • stack navigators - partial support
    • nested stack navigators - V
    • with persistence - V
    • without persistence - V
    • navigation actions supported : navigate, reset, goBack
    • navigation actions support still missing : replace, push, pop, popToTop
  • drawer navigators - no support yet :(
  • tab navigators - partial support , still testing

Installation

yarn add mobx-react-navigation-store
# or with npm 
# npm install mobx-react-navigation-store --save

npm page - https://www.npmjs.com/package/mobx-react-navigation-store

This project depends on other project so if you're using any of the following packages:

  • mobx
  • mobx-persist
  • mobx-react
  • react-navigation

Note that this packages are installed automatically when installing mobx-react-navigation-store with the following versions:

  • "mobx": "^3.4.1",
  • "mobx-persist": "^0.4.1",
  • "mobx-react": "^4.4.1",
  • "react-navigation": "^1.0.0-beta.29"

Usage

Note that more info on the specific methods and fields will be added later in another section this is just general usage instructions

import

this package exports a singelton so every where you import will have the same data inside

import NavigationStore from 'mobx-react-navigation-store'

hydrate store and set navigators

the hydration should happen in the component that renders the main navigator this code is from the react native example app, so assume usage of three navigators called: Main,NavOne,NavTwo where Main is the parent of NavOne and NavOne is the parent of NavTwo and NavTwo is not persistent. you must remember to also import create from mobx-persist like so:

import { create } from 'mobx-persist'
const hydrate = create({
    storage: AsyncStorage //this is since I'm using react native
})
    componentWillMount() {
        hydrate('navigation', NavigationStore).then(() => {
            try {
                NavigationStore.setNavigator('Main', 'MainFirst')
                NavigationStore.setNavigator('NavOne','NavOneFirst','Main')
                NavigationStore.setNavigator('NavTwo','NavTwoFirst','NavOne',false)
                if (!NavigationStore.ActiveNavigator)
                    NavigationStore.setActiveNavigator('Main')
            } catch (err) {
                console.log(err)
            }
            this.setState({hydrated:true})
            setTimeout(()=>NavigationStore.doneHydrating(),1000)
        }).catch(error => console.log(error))
    }
    componentDidMount(){
        if(NavigationStore.storeHydrated)
            NavigationStore.setActiveNavigator('Main')
    }

setting navigation ref and handling actions

in every component that renders a navigator the following must be done inside the render function, here Main is the navigator being rendered:

    <Main
        ref={ref => {
                if (ref &&  (!NavigationStore.getNavigator('Main').navigation || this.state.nowMounted)) {
                    this.setState({ nowMounted: false }) // in componentDidMount => this.setState({ nowMounted: true })
                    try {
                        NavigationStore.setNavigation('Main', ref._navigation)
                    } catch (err) {
                        console.log(err)
                    }
                }}}
        onNavigationStateChange={(oldState, newState, action) => {
                try {
                    NavigationStore.handleAction('Main', action)
                } catch (err) {
                    console.log(err)
                }}}
    /> 

this will ensure that all of the actions are handled by the store and that the methods for navigation in the package will work on this navigator

example stack navigator

right now in order to make a screen marked as a nested navigator you must give it a name including the string: 'NestedNavigator' it can be anything including this string for example : 'NestedNavigator1' or 'NestedNavigator_Chat' or 'NestedNavigatorOne' etc.

const Main = StackNavigator(
    {
        MainFirst: { screen: ScreenOne },
        MainSecond: { screen: ScreenTwo },
        MainThird: { screen: ScreenThree },
        NestedNavigator: { screen: NavigatorOne },
    }, {
        headerMode: 'none',
        lazy: true,
        initialRouteName: 'MainFirst',
    }
)

passing the navigator store via mobx provider

Although as mentioned above, you can just import the navigation store at any screen and it will stay concurrent, you can also pass it via props or via the mobx provider like so:

import { Provider } from 'mobx-react/native'
const stores = { NavigationStore /*add any other stores you want to provide*/ } //assuming you imported NavigationStore

this is inside the render function of the component and as you can see inside the provider is the main navigator and now all of his screens and nested navigators will recieve NavigationStore as a prop

<Provider {...stores}> 
    <Main
        ref={ref => {
                if (ref &&  (!NavigationStore.getNavigator('Main').navigation || this.state.nowMounted)) {
                    this.setState({ nowMounted: false }) // in componentDidMount => this.setState({ nowMounted: true })
                    try {
                        NavigationStore.setNavigation('Main', ref._navigation)
                    } catch (err) {
                        console.log(err)
                    }
                }}}
        onNavigationStateChange={(oldState, newState, action) => {
                try {
                    NavigationStore.handleAction('Main', action)
                } catch (err) {
                    console.log(err)
                }}}
    /> 
</Provider>

notice that in order to react to changes in observables you need the component getting the store to be an observer

actions

setNavigator(name:string, initRoute:string, parent:string, shouldPersist:boolean)

used to declare and set the navigators in the app and their relation to other navigators and should they persist the navigation state params:

  • name - default: none , description: navigator's name , required: yes
  • initRoute - default: none , description: navigator's initial route name , required: yes
  • parent - default: null , description: navigator's parent navigator's name , required: no
  • initRoute - default: true , description: should navigator persist state , required: no

setActiveNavigator(navigatorName:string)

used to set the current active navigator, recommended to use in inside componentDidMount inside the components that renders the navigators params:

  • navigatorName - default: none , description: the name of the navigator to set as active , required: yes

handleAction(navigatorName:string, action:NavigationActionObject)

used to handle the navigations actions to keep correct navigation state, should be used as seen above params:

  • navigatorName - default: none , description: the name of the navigator to be managed , required: yes
  • action - default: none , description: the action triggered , required: yes

goBack(needAction:boolean)

used to go back on screen, even between nested navigators , also helps keep the correctness of the navigation state params:

  • needAction - default: false , description: should the goBack actually go back or just update the state, true is to dispatch the goBack action , required: no

used to navigate between screens params:

  • route - default: none , description: the info of the screen we want to navigate to (based on the navigate of react-navigation) , required: yes
    • routeName - default: none , description: the name of the screen to navigate to , required: yes
    • params - default: none , description: the params for the screen , required: no
    • action - default: none , description: the action to be triggered when getting to the screen , required: no

reset(actions:array, index:number)

used to reset navigation stack , index must be inside the bounds of actions array params:

  • actions - default: none , description: array of NavigationActions to reset stack with , required: yes
  • index - default: none , description: the index of the wanted screen after the stack reset , required: yes

logout()

used to reset all of the navigators in the app to empty stack and initial routes

doneHydrating(ready:boolean, delay:number)

used to hydrate the store and restore the navigation state params:

  • ready - default: true , description: if this is false the stacks will not be restored just the current route will be set to initial route , required: no
  • delay - default: 1500 , description: the delay wanted after restoring all of the stacks before storeHydrated field is set to true , required: no note that the ready param is not ready for use and should be implemented correctly so it could differ between navigators, right now should use only if the predicate used regards the resoration of all navigators

computed - getters

All of this functions are getters and are used as fields like so: for example if the getter is foo()

NavigationStore.foo

returns an array of all the navigators names

AllNavigatorsStacks

returns a map where the keys are navigators names and the values are their stacks

ActiveNavigator

returns the name of the active navigator

CurrentRoute

returns the current route and the name of the navigator he is a screen of sperated by a @, for example: 'MainFirst@Main'

canGoBack

returns a boolean value indicating whether or not it's possible to go back in the navigation

other functions

setNavigation(navigatorName:string, ref:navigationReference)

used to set the navigation reference as seen above params:

  • navigatorName - default: none , description: the name of the navigator to be managed , required: yes
  • ref - default: none , description: the reference to _navigation of a navigator, required: yes

getNavigatorStack(navigatorName:string)

used to get the current stack of a navigator by its name params:

  • navigatorName - default: none , description: the name of the navigator that we want to get his current stack , required: yes

getNavigator(navigatorName:string)

used to get a navigator by its name params:

  • navigatorName - default: none , description: the name of the navigator that we want returned , required: yes Note that the navigator returned is an instance of the class NavigatorPersist which has the following fields and functions:
  • inital fields:
    • navigation = null
    • @persist @observable shouldPersist = true
    • @persist @observable initRoute = null
    • @persist @observable parent = null
    • @persist('list', RoutePersist) @observable currentStack = []
    • @persist('object', RoutePersist) @observable currentRoute = null
  • functions
    • constructor(shouldPersist:boolean, initRoute:string, parent:string)
    • setNavigation(ref:navigationReference)
    • setInitRoute(routeName:string)
    • setShouldPersist(flag:booolean)
    • setRoute(route:{routeName:string,params:object,action:NavigationAction})
    • getter (used like field) - CurrentRoute , returns a string of the current route routeName note that these functions are used by the functions of NvaigationStore, but if you want to use them it's possible
  • @observable storeHydrated = false
  • @persist('map', NavigatorPersist) @observable navigators = new Map()
  • @persist @observable activeNavigator = ''

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

License

This project is licensed under the MIT License

Acknowledgments

this package uses the packages mentioned above, which are great packages that are open source for more info on these packages and how to use them so you can make greater benefit of this package use the links below:

Big thanks to all the people responsible for these projects