While refactoring a section of our React app at HealthTree I stumbled upon a common use-case where a parent route/component could potentially share (part of) his data context with his children components, something like:
| <Route path> | Component | API call |
| /tasks | <TaskList ...> | GET /api/tasks |
| /tasks/:id | <Task ...> | GET /api/tasks/:id |
As a newcomer, the obvious way to handle this in React (& react-router) is to have data fetching logic in both components’ lifecycle methods:
For some use-cases like this one, data fetched in the first component can be re-used for the second one, thus we have an opportunity to potentially save some bandwidth and improve user experience!
After Googling a bit, I found this approach to send state to a route via the Link component:
That’s cleaner, as we only load the task once, but having to use
this.props.location to pass data looks weird, and also this doesn’t solve the fact that it breaks deep linking inside our app! as navigating to the URL or refreshing it will fail to get the state context.
Cross-pollination from Ember
Having been building frontend apps for the past 3+ years mainly using Ember (a highly opinionated web framework), I remembered a common pattern for Ember apps called Nested Index Routes, which basically allows you to have a parent route where data is loaded, and then allows you to find already memoized records in the sub-routes:
Due to the nature of Ember, models attributes memoization is already baked into the framework itself. But React is not an opinionated framework! but a rendering library playing along other libraries such as react-router, therefore as developers we have to decide and implement which patterns to use!
Back to React-lity
In my attempt to emulate Ember’s nested route pattern I learned how to take advantage of React composition by using High Order Components and achieve the same kind of optimization:
I personally like this more!
The reason we need
React.cloneElement Top-Level API is that in that way we can clone new children and pass the appropriate props with our data context.
We could make this even better by using newer Hooks and Context APIs, but that will reman on-hold for another post…
Thanks Beto Cantú for proof-reading and feedback!