react-route-transition-manager
Higher order component to enable loading states between route transitions and fetch data for the new route
Example app
A super simple app example has been created at https://github.com/kellyrmilligan/react-route-transition-manager-example
Why?
There is a lot of boilerplate involved when using react router and fetching the necessary data when transitioning from route to route. This is an attempt to simplify this process with 2 primary features:
- fetch the data that is needed for a new route before rendering the route
- display a loading indicator in the app signifying that the new route is loading, while keeping the current route visible
inspired by https://github.com/ReactTraining/react-router/issues/2101
redux
If you use redux, there is a version of this component with some redux niceness built in.
Wrap your App in the transition manager component
in your top level component, between react router and your app, wrap it's contents with transition manger like so...
const ErrorPage = <div className="Error">Ooops! there was an error...</div> const LoadingIndicator = <div className="Loader">loading...</div> const SplashScreen = <div className='Splashing'><p>welcome to this brave </p></div> const App = <TransitionManager ...props onFetchStart= console onFetchEnd= console onError= console FetchingIndicator=<LoadingIndicator /> ErrorIndicator=<ErrorPage /> SplashScreen=<SplashScreen /> > <Header /> <div className="App"> propschildren </div> </TransitionManager>
this will do a few things. when the route starts to change, it will do the following:
- call
onFetchStart
- render
FetchingIndicator
into the body of the page so you can style it above your app more easily and add a class to thebody
,TransitionManager-body-is-fetching
- loop through the matched handlers looking for fetch methods.
- call the fetch methods, collecting any promises.
- wait for the promises to resolve before rendering the new route if promises are found. if none are found, it will resolve immediately.
- on successful fetching, it will call
onFetchEnd
- on an error, it will call
onError
and render theErrorIndicator
component
Specifying route data needs
This allows you to specify at the route handler level what data that route needs via a static fetch method. The fetching itself should be wired up to redux via thunks, or whatever way you want to handle that. the only requirement is that the static method returns a promise.
static { return { //do something to get the data this route needs, and have it end up in a store somewhere, like a flux store, etc. } } { return <p>the stuff</p> }
Static method params
The reactRouterFetch module is used to call the static methods on the matched route handlers. it will call the fetch method with the react router params(path params) and the query(?id=whatever
)
Props
onFetchStart: PropTypes.func
- This is a function that will be called when fetching starts.
onFetchEnd: PropTypes.func
- This is called when fetching ends
onError: PropTypes.func
- This is called when an error occurs during transition, like a request fails
FetchingIndicator: PropTypes.element
- This will be rendered outside the react component tree into the body, so you can use css to put it above the application.
ErrorIndicator: PropTypes.element
- This will be rendered instead of props.children
when an error occurs.
SplashScreen: PropTypes.element
- This is the element to be shown for the initial page load. your loading indicator may be enough, so this is optional
fetchInitial: PropTypes.bool
- This is for using this in client side apps only, this will initiate a fetch of the route right away, since the data wasn't loaded from the server.
showIndicatorOnInitial
- This prop will control whether or not you want to also show your loading indicator on the initial load. Depending on your ui, you may want to have a splash screen with a loading bar at the top of the page or something.
Still to do:
- if your API returns an error that you want to handle more specifically, you need to do it in your error Indicator and access your redux store. While this is serviceable, I want to provided a
pass-through
, where if an error happens on a certain route, you will be able to handle the error in the route handler instead if you want to.