Skip to content

Instantly share code, notes, and snippets.

@olivergeorge
Last active May 7, 2018 22:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save olivergeorge/912eb706ce9c9907e2fe97cf76c67c6c to your computer and use it in GitHub Desktop.
Save olivergeorge/912eb706ce9c9907e2fe97cf76c67c6c to your computer and use it in GitHub Desktop.

Navigating between scenes

React Native lets us render views but doesn't solve the problem of transitioning between scenes.

Navigation concerns include:

  • rendering common elements (headers)
  • rendering navigation elements (back button)
  • keeping track of global history stack
  • navigating between scenes
  • animations when moving between scenes
  • hiding the keyboard when navigating
  • debouncing
  • opening / closing the drawere

Tradeoffs

We're using React Navigation v1.5 as it's intended to be used which means some tradeoffs.

Unanswered / unresolved

  • How subscriptions can include navigation state data. We could use state navigator.addListeners to act on focus/blur events.
  • Including a drawer menu using DrawerNavigator.
  • React Navigation v2 became stable yesterday(!) so needs a review of breaking changes and update as necessary.
  • Sprinkling clj->js and js->clj on utils

Dependency

Firstly we need to update our packages.json to include a new depencency

yarn add react-navigation

API

Updates to rn-api to give us access to the navigation api.

Utils

The re-frame integration will be based on some helper functions. These don't yet take care of interop so our code can work with standard clojure data structures.

Effects

Our event handlers will use effects to cause navigation changes.

Co-Effects

To see the navigation state, event handlers can use a co-effect.

Components

React Navigation works as a top level component configured with routing details. Each new route will need to be added here. We get a ref to the navigator when the top level navigation component is rendered. We use a SwitchNavigator for authentication flows and a StackNavigator for application routes.

And we replace the default re-natal app-root with our new one from navigation-component.

Note: we're keeping the future-app.ios.core/app-root var because the figwheel reloading mechanism uses it.

Events

We prefer specific events handlers which involve navigation instead of generic use handlers which would tempt us to put behaviour directly in our views.

We do add an empty :app/navigation-ready event to allow initialisation work to be done after the navigator is ready.

(ns future-app.events.app-events
(:require [re-frame.core :as re-frame]))
(reg-event-fx
:app/navigator-ready
(fn [_ _]))
(ns future-app.ios.core
(:require ...
[future-app.components.navigation-components :as navigation-component]))
(def app-root navigation-component/app-root)
(defn init []
(dispatch-sync [:initialize-db])
(.registerComponent app-registry "FutureApp" #(r/reactify-component app-root)))
(ns future-app.coeffects.navigation-coeffects
(:require [future-app.utils.navigation-utils :as navigation-utils]
[re-frame.core :as re-frame]))
(re-frame/reg-cofx :navigator/state navigation-utils/state)
(ns future-app.components.navigation-components
(:require [reagent.core :as r]
[future-app.rn-api :as rn-api]
[future-app.utils.navigation-utils :as navigation-utils]
[re-frame.core :as re-frame]))
(def AppRouteConfigs
#js {})
(def AuthRouteConfigs
#js {})
(def auth-loading-screen []
[:> rn-api/View {:styles {:flex 1 :alignItems "center" :justifyContent "center"}}
[:> rn-api/ActivityIndicator]])
(def RootStack
(rn-api/SwitchNavigator
#js {:AuthLoading (r/reactify-component auth-loading-screen)
:App (rn-api/StackNavigator AppRouteConfigs)
:Auth (rn-api/StackNavigator AuthRouteConfigs)}
#js {:initialRouteName "Auth"}))
(defn handle-navigator-ref [navigator]
(navigation-utils/init navigator)
(re-frame/dispatch [:app/navigator-ready]))
(defn app-root []
[:> RootStack {:ref handle-navigator-ref}])
(ns future-app.effects.navigation-effects
(:require [re-frame.core :as re-frame]
[future-app.utils.navigation-utils :as navigation-utils]))
(re-frame/reg-fx :navigator/navigate navigation-utils/navigate)
(re-frame/reg-fx :navigator/back navigation-utils/back)
(re-frame/reg-fx :navigator/push navigation-utils/push)
(re-frame/reg-fx :navigator/pop navigation-utils/pop)
(ns future-app.utils.navigation-utils
(:refer-clojure :exclude [pop replace])
(:require [future-app.rn-api :as rn-api]))
(def navigator nil)
(defn init [n]
(set! navigator n))
(defn navigate
[{:keys [routeName params]}]
(.dispatch navigator (.navigate rn-api/NavigationActions #js {:routeName routeName :params params})))
(defn replace
[{:keys [key newKey routeName params]}]
(.dispatch navigator (.replace rn-api/NavigationActions #js {:key key :newKey newKey :routeName routeName :params params})))
(defn back [] (.dispatch navigator (.back rn-api/NavigationActions)))
(defn push [] (.dispatch navigator (.push rn-api/NavigationActions)))
(defn pop [] (.dispatch navigator (.pop rn-api/NavigationActions)))
(defn state [] (.-state navigator))
...
(defonce ReactNavigation (js/require "react-navigation"))
(defonce NavigationActions (gobj/get ReactNavigation "NavigationActions"))
(defonce StackNavigator (gobj/get ReactNavigation "StackNavigator"))
(defonce TabNavigator (gobj/get ReactNavigation "TabNavigator"))
(defonce DrawerNavigator (gobj/get ReactNavigation "DrawerNavigator"))
(defonce SwitchNavigator (gobj/get ReactNavigation "SwitchNavigator"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment