Browser History

On native, it's up to you what happens when the user presses the back button. But on the web, the browser back button always steps back to the previously visited URL. Because browser history is linear, you can only connect it up to one NavigationStack. For all your other stacks you should disable the history manager of the associated stateNavigator.

stateNavigator.historyManager.disabled = true;
stateNavigator.historyManager.stop();

Note

If your app has tabs that each contain stacks, then it doesn't make sense to connect just one of them to browser history. Instead you should disable the history manager of all your stacks. That way your app will still run, it's just that the Url won't change.

Cleaning Urls

By default, the Navigation router stores the stack of scenes in the Url. In our example app, when a user opens an email with an id of 12 the Url is '/mail?id=121_2&crumb=%2Finbox'. The crumb parameter in the query string marks that the 'inbox' is the first scene in the stack. We hide the stack from the Url by passing a NavigationStack.HistoryManager into the StateNavigator constructor. The HistoryManager turns the Url that opens the email into '/mail?id=121_2'.

const stateNavigator = new StateNavigator([
  {key: 'inbox'},
  {key: 'mail', trackCrumbTrail: true},
  {key: 'compose', trackCrumbTrail: true}
], NavigationStack.HistoryManager && new NavigationStack.HistoryManager());

Configuring Types

The Navigation router keeps track of data types so we don't have to convert the email id back to a number on the 'mail' scene. By default it uses the Url to keep track of this type information. In the example Url the last three characters track that the id is a number. We'll remove these trailing characters, so that the Url reads '/mail?id=12' instead of '/mail?id=121_2', by assigning the id a type of 'number'. The other supported types are 'boolean', 'date', 'stringarray', 'booleanarray' and 'datearray' ('string' is the default).

[
  {key: 'inbox'},
  {key: 'mail', defaultTypes: {id: 'number'}, trackCrumbTrail: true},
  {key: 'compose', trackCrumbTrail: true}
]

Configuring Routes

You don't have to settle for the default Urls generated by the Navigation router. You can have whatever Url scheme you want. To change our Url from '/mail?id=12' to '/open/12' we configure a route with the id as a route parameter.

[
  {key: 'inbox'},
  {key: 'mail', route: 'open/{id}', defaultTypes: {id: 'number'}, trackCrumbTrail: true},
  {key: 'compose', trackCrumbTrail: true}
]

Patching Bookmarks

A user visiting a bookmarked email will find that they're taken to their inbox instead of the email. That's because we've hard-coded the startup navigation. That works for native but on the web we need to use the incoming Url to decide where to go. The start function on the stateNavigator does this for us.

if (Platform.OS === 'web') {
  stateNavigator.start();
}

Now the user is taken to their email but the button taking them back to their inbox is disabled. The bookmark takes the user straight to their open email, bypassing the inbox, so the stack of scenes is empty and they can't navigate back. We can re-enable the button by prepopulating the stack so it looks like the user came from the 'inbox' scene. We can build this Url by using the Navigation router's fluent navigation to simulate the user opening the email from their inbox.

const url = stateNavigator.fluent()
  .navigate('inbox')
  .navigate('mail', {id: 12}).url;

We change the startup Url by passing a function into the NavigationStack.HistoryManager constructor. The function receives the browser Url and we use fluent navigation to return the new Url with the 'inbox' scene included. We extract the email Id from the incoming Url using the parseLink function on the stateNavigator.

new NavigationStack.HistoryManager(url => {
  const {state, data} = stateNavigator.parseLink(url);
  return stateNavigator.fluent()
    .navigate('inbox')
    .navigate(state.key, data).url;
})

Removing Hashes

By default, the HistoryManager operates in hash history mode. For example, the full Url that opens an email of id 12 is 'https://domain/#/mail?id=121_2'. To remove the hash and clean up the Url so it reads 'https://domain/mail?id=121_2', we pass an empty string as the second parameter to the HistoryManager constructor. If we wanted our Url to look like 'https://domain/app/mail?id=121_2', we'd pass the 'app' base path as the second parameter instead.

new NavigationStack.HistoryManager(null, '')