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

Preload dependencies during idle time? #7

Open
oliviertassinari opened this issue Jan 23, 2017 · 3 comments
Open

Preload dependencies during idle time? #7

oliviertassinari opened this issue Jan 23, 2017 · 3 comments

Comments

@oliviertassinari
Copy link

oliviertassinari commented Jan 23, 2017

Hey, cool to see you have abstracted away the lazy loading handling for large React tree.
I found the approach much better than what react-router is suggesting to do!
I'm wondering, do you have any plan preloading dependencies on the client side during the idle time?

I have been implemented a naive solution, I'm pretty sure we could improve that. It's preloading everything.
If we were collecting some data. For instance, the order of lazing loading, we could even build a prediction model. I have heard that Facebook is using a machine learning algorithm to smartly load dependencies ahead of time.

@ctrlplusb
Copy link
Owner

Hey @oliviertassinari! Thanks!

Yeah, totally a great idea and I think @bradennapier has some alternative ideas around this problem.

It would be great to come up with a simple and flexible solution for this. I'll review your solution when I get a chance and keep this in the back of my mind. Happy to collaborate with you to get something awesome together.

@bradennapier
Copy link

bradennapier commented Jan 24, 2017

Olivier, I may be misunderstanding your intent - but the fact the async loading is done using import which already returns promises, we shouldn't need to use setTimeout to accomplish nested asynchronous loading.

This method also allows using the defer method to handle pushing to the client for rendering which @ctrlplusb has given us. From my testing it appears to do a good job so far, but I haven't done extensive tests on it as its working for our dev thus far.

I have setup a method of handling routes asynchronously as well as their dependencies.

export default [
  {
    id: 'Home',
    exactly: true,
    props: {
      title: 'Home'
    },
    pattern: '/',
    component: () => import('./screens/Home')
  },
  {
    id: 'Welcome',
    props: {
      title: 'Welcome'
    },
    pattern: '/login/1',
    component: () => import('./screens/Welcome')
  },
  {
    id: 'SecurityCenter',
    props: {
      title: 'Security Center'
    },
    pattern: '/security-center',
    secure: true,
    component: () => import('./screens/SecurityCenter')
  },
  {
    id: 'UserProjects',
    props: {
      title: 'Project Select'
    },
    pattern: '/project',
    component: () => import('./screens/UserProjects')
  },
  {
    id: 'LoginPage',
    props: {
      title: 'Login'
    },
    pattern: '/login',
    component: () => import('./screens/Login')
  }
]

Then when we move deeper into the routes we simply pass new routes that will be also imported asynchronously

import React from 'react'
import Resolver from 'app/resolver'

const routes = [
  {
    id: 'ProjectSelect',
    pattern: '/',
    exactly: true,
    component: () => import('./screens/ProjectSelect')
  },
  {
    id: 'ProjectDashboard',
    pattern: '/dashboard/:projectSlug',
    component: () => import('./screens/ProjectDashboard')
  }
]

export default props => (<Resolver {...props} routes={routes} />)

Then we have this as a Resolver ( I added a redux push as well for redirecting when accessing secure pages using a layer:

const RoutedResolver = ({ routes, ...props }) => (
  <div>
    {
      routes.map( route => {
        if ( ! route.id ) { throw new Error('Route Does not have an ID: ', route) }
        const matchProps = {
          ...route,
          key: route.id,
          exactly: route.exactly == true,
          pattern: route.pattern === '/'
            ? props.pathname
            : props.pathname + route.pattern
        }
        return <Match {...matchProps} render={ renderProps => {
            const appProps = {
              ...matchProps,
              ...renderProps,
            }
            return <Resolver {...props} {...appProps} />
          }} 
        />
      })
    }
  </div>
)

const Loading = () => (<div>LOADING</div>)

const Resolver = ({ 
  component, routes, 
  isAuthenticated = false,
  defer  = false, 
  secure = false,
  ...props 
}, { store }) => {
  if ( secure && ! isAuthenticated ) {
    const { location, ...rest } = props
    store.dispatch({
      type: 'ROUTER_SECURE_REQUESTED',
      location: props.location,
      props:    rest
    })
    return null
  }
  const Component = routes
    ? RoutedResolver
    : component
      ? BuildAsyncComponent(component, secure || defer)
      : Loading
  if ( routes ) { props.routes = routes }
  //console.log('Resolver: ', props.id, props)
  return <Component {...props} />
}

Resolver.contextTypes = {
  store: PropTypes.object.isRequired
}

export const BuildAsyncComponent = (component, defer) => 
  createAsyncComponent({
    resolve: component,
    defer
  })

export default Resolver

@ctrlplusb
Copy link
Owner

Related: ctrlplusb/react-universally#406

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

3 participants