Skip to content

Instantly share code, notes, and snippets.

@jaysoo
Last active October 12, 2017 03:54
Show Gist options
  • Save jaysoo/aa0c50ef05b4454feea3f4837997e9b0 to your computer and use it in GitHub Desktop.
Save jaysoo/aa0c50ef05b4454feea3f4837997e9b0 to your computer and use it in GitHub Desktop.
examples showing how ssr + lazy components might work better
/*
* This is just a scratch of ideas I've been thinking about when it comes
* to lazy-loaded components/modules, and how that can work with rendering a react
* application in a single pass.
*
* Note: Don't take any of these ideas too seriously. I have implemented some of them in experiments,
* but much more thinking is needed.
*/
/*
* components/Button.js
*/
import styled from 'styled-component'
const Button = styled.button`
/* ... */
`
/*
* components/Text.js
*/
import styled from 'styled-component'
const Text = styled.span`
/* ... */
`
/*
* components/index.js
*/
import { lazy, concat } from '???'
// Each `lazy` call makes a loadable "module".
const Button = lazy(() => import('./Button'))
const Text = lazy(() => import('./Text'))
export { Button, Text }
// Use a combinator to concat both "modules"
export default concat(Button, Text)
/*
* index.js
*/
import React from 'react'
import ReactDOM from 'react-dom'
import { load } from '???'
import cmps, { Button, Text } from './components'
// Wait to resolve module(s) before rendering the application.
load(cmps).then(m => {
// Maybe we should explicitly pull components out of module?
// This can be pretty verbose... but HOCs, etc. may be a ble to help.
const [Btn, Txt] = m.get(Button, Text)
ReactDOM.render(
<div>
{/* But maybe it's better/easier to synchonously resolve component function once it has been loaded. */}
<Button>Button 1</Button>
<Txt>Hello</Txt>
<Btn>Button 2</Btn>
</div>,
document.getElementById('root')
)
})
/*
* ------------------------------------------------------------------*
* Using these "modules", you can compose the entire app the same way.
*/
/*
* foo/index.js
*/
import { lazy, concat } from '???'
const A = lazy(() => import('./A'))
const B = lazy(() => import('./B'))
const C = lazy(() => import('./C'))
export { A,B,C }
export default concat(A, B, C)
/*
* bar/index.js
*/
import { lazy, concat } from '???'
const D = lazy(() => import('./D'))
export { D }
export default D
/*
* quux/index.js
*/
import { concat } from '???'
import foo from '../foo'
import bar, { D as E } from '../bar'
export { E } // re-export stuff if we want to
export default concat(foo, bar)
/*
* index2.js
*/
import quux, { E } from './quux'
import { A, B, C } from './foo'
load(quux).then(() => {
// Can render synchronously since modules have been resolved already.
ReactDOM.render(
<div>
<A/>
<B/>
<C/>
<D/>
<E/>
</div>,
document.getElementById('root')
)
})
/*
* ------------------------------------------------------------------*
* Other thoughts...
*
* - Knowing which modules are loaded is very much a routing concern (IMO). I think all the required modules
* can known during application bootup, so there should just be one promise resolution before rendering.
*
* - I'm debating whether having "smart" components that has side-effects (i.e. `import()`) is the way to go.
* Perhaps it's better to reify the module concept, and keep components dumb. This reduces the number of areas
* in which side-effects can occur, and also keeps testing components simple.
*
* - It's possible to use higher-order functions or some other mapping means to spy on what modules are loaded
* given a route. This means that we can easily record a list of entry-point modules to include on the page
* using <script> elements. This speeds up the initial render time.
*
* - Where to handle errors? Can be at a component-level or a module-level, or both. The granularity depends
* on the domain, but error cases should always be considered (even something like unreliable network).
*
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment