Set up the Router

There is zero setup needed for ReasonReact.Router. The Reason React package includes it by default. The Router has only three functions. You do not have to wrap your whole application into with a <Router> component.

Read the URL hash when page is loaded for the first time

As per documentation https://reasonml.github.io/reason-react/docs/en/router.html#directly-get-a-route, there is a way to get initial URL in a dangerous way: ReasonReact.Router.dangerouslyGetInitialUrl(). This call should be used only during an initial start of application:

let make = (_) => {

...component,

initialState: () => {

cityName: ReasonReact.Router.dangerouslyGetInitialUrl().hash,

pressures: [||],

failed: false,

errorMessage: None

},

Here we get the URL entered by the user and set it into our initial state. Just as intended.

Listen to URL changes

ReasonReact’s Router has two methods which you can use to watch for URL changes: watchUrl and unwatchUrl.

According to the documentation, we can combine them with ReasonReact subscriptions, which removes all boilerplate code like: listenTo/stopListeningTo, setInterval/clearInterval, etc.

Let’s add the subscriptions property to our Component:

let make = (_) => {

...component,

initialState: () => {

cityName: ReasonReact.Router.dangerouslyGetInitialUrl().hash,

pressures: [||],

failed: false,

errorMessage: None

},

subscriptions: self => [

Sub(

() =>

ReasonReact.Router.watchUrl(url => self.send(UpdateCity(url.hash))),

ReasonReact.Router.unwatchUrl

)

],

Each time the user updates the URL, the subscription will issue a Redux action UpdateCity with the new CityName. We do not have to unsubscribe manually, ReasonReact expects seconds parameter to be the unsubscribe function.

Update the URL after user action in application

Our Redux action UpdateCity updates the state of the MainApp component and issues another Redux Action: LoadPressure. Let’s push a new state to the browser history, when a user clicks on “Get Pressure Changes” button:

reducer: (action, state) =>

switch action {

| UpdateCity(s) =>

ReasonReact.UpdateWithSideEffects(

{...state, cityName: s},

(

self => {

if (s != state.cityName) {

ReasonReact.Router.push("/#" ++ s);

} else {

self.send(LoadPressure);

}

}

)

)

ReasonReact.UpdateWithSideEffects is used when we want to update the state of a component and then issue some other action. Pay attention to

if (s != state.cityName) {

If we omit this check, then ReasonReact.Router.push will modify the URL with a new hash, which triggers the subscription for watchUrl, which will execute the UpdateCity Redux action again, which will push another state to the history… and so on. Without this check we will get this nice error message at runtime:

Uncaught (in promise) Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

Also note, that in

if (s != state.cityName) {

we check against the state variable, which is an input parameter to the reducer function. We do not write:

if (s != self.state.cityName) {

as self.state.cityName will be already updated with the ‘s’ variable value and this ‘if’ statement becomes meaningless.

Final result

Summary