react-page-loader-redux

PageLoader is a React Component that handles rendering your react-router routes.

Usage no npm install needed!

<script type="module">
  import reactPageLoaderRedux from 'https://cdn.skypack.dev/react-page-loader-redux';
</script>

README

Page Loader

PageLoader is a React Component that handles rendering your react-router routes.

It's built with the AppShell model in mind: Structuring your app in a way do that all pages share a common 'shell'.

Typically with an app-shell model, you only fetch the data you need for the current page - when the user navigates to a new page you then fetch the data / js /css for that page.

Here in lays the problem.

Co-ordinating this transition, so that you don't have to write lots of imperative code.

PageLoader combines the following tools so you don't have to:

  • react
  • react-router
  • redux

Usage

<PageLoader routes={routeConfig} ErrorPage={ErrorPage} OfflinePage= {OfflinePage} />

Route Config

Before you may have been rendering your routes like so:

<Router>
  <Route exact path="/" component={HomePage} />
  <Route exact path="/about" component={AboutPage} />
</Router>

With react-page-loader-redux, we use react-router-config to render the routes.

It take the routes, and an array of objects like so:

const routes = [
  {
    exact: true,
    path: '/',
    component: HomePage
  },
  {
    exact: true,
    path: '/about',
    component: AboutPage
  }
]

If you don't need to fetch data for a page, or the page component already exists in the app-shell js bundle then keep the config like above.

Fetching data for a page

If you need to fetch data before rendering a page do so by setting a fetcher function in the route config:

const routes = [
  {
    path: '/info',
    component: InfoPage,
    fetcher: ({ match, route }) => { /* return a promise */}
  }
]

The fetcher will receive an object argument with the following keys:

  • match: the matched route and it's params
  • route: a reference to the route config object itself

The fetcher must return a promise - the promise should resolve the data required for that page.

PageLoader will handle the promise and store the data in redux under pageLoader.data.

Fetching JS for a page

Now a days its common practice to split your JS into multiple bundles, since it's less JS to send down the wire and parse this is a nice perf boost for the end user.

We like react-loadable for this use case:

cons loadable = require('react-loadable')

const routes = [
  {
    path: '/info',
    component: loadable({
      // using dynamic import
      // webpack will automatically create this as a split point
      // you can name the bundle using an inline comment
      loader: import(/* webpackChunkName: "info-page" */'./pages/InfoPage'),
      loading: <span>loading...</span>
    }),

  }
]

Server Side Rendering

  • If you are using separate JS bundles, these bundles need to be loaded ahead of time before you render, or else you'd just get a 'loading' page.
  • If you are using react-loadable then you can use preloadAll().then(startServer)
  • When the PageLoader boots up, it will detect the browsers location and begin fetching the required assets before trying to render that page. TODO Client JS logic for preloading.

AppShell Booting

  • With service workers you can cache the app-shell html, and handle navigation requests allowing you to return the cached app-shell html instead of going to the network.
  • The PageLoader can detect its booting via the app-shell, also being fetching the required assets before rendering the browsers location.

Client Side Transitions

  • React-router's <Link /> component handles updating the location and preventing the browsers default behaviour
  • PageLoader will handle fetching the assets before making the transition

Store props

TODO document integration

{
  renderedRoute: Object,
  isFetching: Boolean,
  isOffline: Boolean
  error: Error Object,
  data: Object
}