Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finalize prefetching and code splitting #13

Open
faceyspacey opened this issue Nov 7, 2018 · 2 comments
Open

Finalize prefetching and code splitting #13

faceyspacey opened this issue Nov 7, 2018 · 2 comments

Comments

@faceyspacey
Copy link
Member

The idea was to finish the code splitting middleware, and then implement a prefetch mechanism that simply calls a route’s pipeline without actually changing the route. The code splitting middleware will automatically acquire necessary chunks, and any data fetched from thunks would be retrieved.

So step 1 is a simple api like: Rudy.prefetch(actionOrPath). And its pipeline is called, but a flag insures the URL doesn’t change. It’s up to the developer to insure that their thunks use Params from actions, not state, as the route won’t have changed yet. This is the primary thing to understand for apps that want to implement prefetching.

@faceyspacey
Copy link
Member Author

Step 2 is our link component can have a prefetch prop like Next.js, and/or we can do one better and add a key to each route such as route.nextRoutes, which is a function that returns an array of actions or paths to prefetch. This function is called after entering the route, after its pipeline is complete.

@hedgepigdaniel
Copy link
Collaborator

The prefetching thunks part makes alot of sense to me. I imagine that you could trigger prefetching by dispatching an action like

{
  type: DASHBOARD,
  prefetch: true,
}

or

{
  type: '@@rudy/PREFETCH',
  payload: {
    type: DASHBOARD,
  },
}

So the prefetch action would go through the pipeline, and then if the navigation actually happens, then the commit action would also go through the pipeline.

Then its a case of the middlewares needing some tweaks:

  • The enter and setPageTitle middlewares should skip prefetch actions
  • The call middleware should probably pass some sort of arg to the callbacks indicating that this is a prefetch
  • Maybe the user can specify whether each instance of call or other middlewares includes prefetch actions, e.g. like call('onAfterChange', { prev: true, onPrefetch: true, afterPrefetch: false })

Maybe if there is something about the request, say req.isPrefetching() and req.wasPrefetched() then the middlewares can skip when they need to. There probably also needs to be a way of clearing the prefetch cache - some sane default behaviour and a way for the user to manually clear it.

The problem I often run into when thinking about the chunks part is how do you determine which chunks you need for a route so that the code splitting middleware can load them, and still:

  • Have a clear separation of concerns between React/View, Model/Redux, and Controller/Rudy
  • Not have to do lots of manual effort designing chunk structure (for me this has always been a huge amount of effort for very small benefit)

The chunks needed for the thunks is easy enough - I like to do it this way:

// routesMap.js
{
  DASHBOARD: {
    thunk: (dispatch, getState, bag) => import('./loadDashboard').then(
      // Everything needed by loadDashboard is in its own chunk(s) now
      ({ loadDashboard }) => loadDashboard(dispatch, getState, bag))
    ),
  },
}
// loadDashboard.js
import {dashboard } from './reducers/dashboard';

import { Dashboard } from './components/dashboard';

export const loadDashboard = (dispatch, getState, bag) => {
  // Add any reducers you need
  bag.extra.addReducer('dashboard', dashboard);

  // preload any components you need
  Dashboard.preload();

  // This generic component happens to be used by dashboard... do I really need to care about that now?
  LoadableAutoFill.preload();

  return API.request();
};

So that's easy enough - if you run the callbacks, the correct chunks are loaded for the controller and model. I think this needs something extra to hydrate the root reducer if addReducer is called in SSR, but I think that's easily solveable.

For me it feels a little bit dodgy to be importing react components (view code) from the controller. The developer would have to manually keep the react components imported here in sync with the components that end up actually being used in practice to render the route. I wonder if there's a better way to handle that? I think this way can work, its just not very DRY to have a nice automatically code split component tree and then have to hard code all the split points again in the thunk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants