Blip's usage of React Router
When development of blip in its current form as a React application began in late 2013 and the beginning of 2014, React Router did not yet exist, at least not as a well-supported and widely-used client-side routing solution for React applications.
<App/> component back then at the top level that was 1000s of lines long (😱), and app state was basically all contained in the state of this component, except for the stuff that director managed, if it was truly the source of truth for that state.)
✨ Today ✨
We ripped apart the old
<App/> component and refactored to bring in React Router as our routing solution in late 2015. Routes are defined in app/routes.js. We make extensive use of the
onEnter hook for React Router
<Route/> elements, including the asynchronous option when a callback function is provided as the third argument; this blocks the route transition until the callback is called.
We also sometimes pass our redux store to an
onEnter hook function in order to access the application state via
store.getState() in order to determine whether to continue the current route transition or redirect (depending on whether the logged-in user has verified their sign-up e-mail address or not, for example).
State changes that require URL changes are rare in blip currently (largely because we simply don't sync very much application state with the URL). The vast majority of the changes happen in the
onEnter hooks defined in
routes.js, but there are a few other places where we redirect in response to user interaction with
git grep-ing for this phrase should find all instances for you) or where we redirect inside a "thunk" action creator; the latter instances can be found with a search for
🚀 The Future
More visualization state in the URL ("deep linking")
One of our goals for the incremental rewrite of Tidepool's data visualization code (from tideline to viz) is to enable "deep linking" in the data visualization(s) so that a user can bookmark or copy and share a URL and anyone who has that URL (and permissions to view the relevant PwD's data) will see essentially the same data visualization that the original user saw.
The vast majority of the data visualization state, including which of the five "views" the user is on (Basics, Daily, Weekly, Trends, or Device Settings) is currently encapsulated inside the
<PatientData/> component as its React-internal
As we're rewriting visualization code, we're adding new state—i.e., that didn't exist before, such as state belonging to the new CGM version of the Trends view and the state of which basal schedules or settings profiles are open or collapsed on the Device Settings view—to blip's redux-controlled state by way of a
vizReducer as one of the exports from the new viz repo. By including this reducer in our composed root reducer for blip, we ensure that blip's state updates in response to any of the action creators fired in viz components (or in blip, where we import action creator functions themselves or component from viz to be rendered in blip).
As we move more and more visualization code into viz and especially as we move functionality controlling some aspects of the visualization state (such as which view is active) from blip into viz, we'll have to plan for the eventual goal of deep-linking or, in other words, think carefully about where the source of truth for certain state should live.
Take the active view, for example, which in the current
<PatientData/> state, we track as
chartType. This state will definitely need to be part of the URL as the first step along the road to "deep linking": we want URLs of the form
/patients/:id/data/daily instead of the current
/patients/:id/data for all views. In order to avoid duplication of statea between the
routing branch of blip's state (updated via the
routeReducer which is an export from the react-router-redux package in a fashion directly parallelb to our export of
vizReducer from the @tidepool/viz package) and the
viz branch of state controlled by the
vizReducer, we'll need to use a different strategy—that is, not the current
vizReducer strategy—to update the
chartType state so that this state is properly "owned" by the
There may be several ways of solving the problem of affecting
routing state from viz, which has no direct access to blip's router (and nor should it). A couple to consider are:
- viz dispatches actions representing state changes that are reflected in the URL (such as the active view) as per usual in a Redux implementation, but no state changes are defined for these action types in the exported
vizReducer; instead, blip intercepts all such actions (according to their
type) with a middleware and dispatches a
routeActions.pushaction to update the
- viz includes React Router as a dependency and the top-level component exported to blip requires a set of
hrefs as props to populate its React Router
Upgrading to the 4.x version of React Router
The 4.x version of React Router is, as of mid-November 2016, currently in alpha and includes many breaking changes. The API changes have already caused some controversy in the community, so we'll have to watch the development and assess whether or not it makes sense to upgrade our dependency in blip. Since React Router is a true community project (i.e., not released by Facebook), it seems unlikely that the 3.x version will enjoy long-term support.
a. As a known issue/remaining tech debt from the introduction of React Router and Redux into the blip codebase, we currently have some duplication of information across the
blipbranches of blip's state tree. A good example is the user ID hash of the PwD whose data is in view on the
/patients/:id/datapath currently used for all data views; this ID is part of the
routingstate as the
:idparam in the path, but it is also set as the
blipbranch of the state tree upon successful fetching of the PwD's profile. ↩
b. And, in fact, one of the inspirations for the way we structured the @tidepool/viz package along with the redux-form package. ↩