Skip to content

Instantly share code, notes, and snippets.

@sidferreira
Last active February 8, 2022 13:42
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sidferreira/72e17d29b224ca93cf86901ed1ceb032 to your computer and use it in GitHub Desktop.
Save sidferreira/72e17d29b224ca93cf86901ed1ceb032 to your computer and use it in GitHub Desktop.
React Navigation + MobX Integration (React Native Based)
// App/index.js
import React, { Component } from 'react'
import { View, StyleSheet } from 'react-native'
import { initStore, Provider } from '../Store'
import RootContainer from '../Containers/RootContainer'
import RootNavigator from './RootNavigator'
const store = initStore({RootNavigator})
export default () => (
<View style={styles.container}>
<Provider store={store} >
<RootContainer />
</Provider>
</View>
)
const styles = StyleSheet.create({
container: {
flex: 1
}
})
// Store/navStore.js
import Firebase from 'react-native-firebase'
import { NavigationActions } from 'react-navigation'
import { observable, action, toJS } from 'mobx'
import invariant from 'invariant'
const DEFAULT_ROUTE = 'Launch'
export default class NavStore {
@observable state = {
index: 0,
routes: [{ key: DEFAULT_ROUTE, routeName: DEFAULT_ROUTE }]
}
lastState = null
@observable currentRoute = DEFAULT_ROUTE
@observable currentSubRoute = null
subscriberRoot = null
constructor (rootNavigator, subscriberRoot = 'root') {
this.rootNavigator = rootNavigator
this.subscribers.set(subscriberRoot, new Set())
this.addListener = this.createAddListener(subscriberRoot)
this.subscriberRoot = subscriberRoot
}
reset = (routeName, params, action, key) => this.dispatch(NavigationActions.reset({ index: 0, key: null, actions: [NavigationActions.navigate({ routeName, params, action })] }))
goToSessionModal = (actionType) => this.dispatch(NavigationActions.reset({ index: 1, actions: [{ routeName: 'Internal' }, { routeName: 'Session', params: { actionType } }] }))
go = (routeName, params) => this.dispatch(NavigationActions.navigate({ routeName, params }))
back = () => this.dispatch(NavigationActions.back())
@action('NAV:dispatch')
dispatch = (action, stackNavState = true) => {
let currentState = toJS(this.state, false)
let state = null
if (action.type !== NavigationActions.COMPLETE_TRANSITION) {
state = this.rootNavigator.router.getStateForAction(action, currentState)
let currentRoute = ''
let currentSubRoute = ''
let route = {}
if (state.index < state.routes.length) {
route = state.routes[state.index]
currentRoute = route.routeName
currentSubRoute = route.routes && route.routes[route.index].routeName
}
if (currentRoute !== this.currentRoute || currentSubRoute !== this.currentSubRoute) {
// console.log(`RouteAccepted ${currentRoute} ${currentSubRoute}`)
this.currentRoute = currentRoute
this.currentSubRoute = currentSubRoute
this.state = state
this.lastState = currentState
Firebase.analytics().setCurrentScreen(this.currentSubRoute || this.currentRoute)
}
}
const subscribers = this.subscribers.get(this.subscriberRoot)
this.triggerAllSubscribers(subscribers, {
type: 'action',
action,
state: state ? toJS(state, false) : currentState,
lastState: this.lastState
})
}
subscribers = new Map();
createAddListener (key) {
invariant(
this.subscribers.has(key),
"Cannot listen for a key that isn't associated with a Redux store. " +
'First call `createReactNavigationReduxMiddleware` so that we know ' +
'when to trigger your listener.'
)
return (eventName, handler) => {
if (eventName !== 'action') {
return { remove: () => {} }
}
const subscribers = this.subscribers.get(key)
invariant(subscribers, `subscribers set should exist for ${key}`)
subscribers.add(handler)
return {
remove: () => {
subscribers.delete(handler)
}
}
}
}
triggerAllSubscribers = (subscribers, payload) =>
subscribers.forEach(subscriber => subscriber(payload))
}
// App/RootNavigator.js
import { StackNavigator } from 'react-navigation'
import AScreen from '../Containers/AScreen'
import BScreen from '../Containers/BScreen'
const RootNavigator = StackNavigator(
{
A: { screen: AScreen },
B: { screen: BScreen },
}
)
export default RootNavigator
// Store/index.js
import { useStrict } from 'mobx'
import NavStore from './navStore'
export { Provider } from 'mobx-react'
useStrict(true)
export class RootStore {
nav:NavStore
constructor ({RootNavigator}) {
this.nav = new NavStore(RootNavigator)
}
}
export const initStore = (options) => new RootStore(options)
@MrShakes
Copy link

MrShakes commented Feb 29, 2020

This doesn't work for react-navigation greater than 2.2.0, any updates?

@sidferreira
Copy link
Author

@MrShakes did this like 2 or 3 years ago and I'm back to redux. I think navigation service is a better approach too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment