How I spent my Christmas enabling SSR

tl;dr

  1. Use componentWillMount for dispatches
  2. Handle async sagas via END channel
  3. Use channels for dependent async requests
  4. Utilise lazy-loading
  5. Manage components rendered on the server
  6. Defer the bundle
  7. Profiling
  8. Results

1. Use componentWillMount for dispatches

function init(x) {
return dispatch(someAction(x));
}
Component.init = init;
componentWillMount() {
this.props.someAction(this.props.x);
}

2. Handle async sagas via END channel

  • run all sagas (root yields all watchers),
  • render to seed store (kick off the initial actions),
  • wait for all sagas (including async sagas) to finish,
  • render the app again with the correct data in the store.
store.runSaga(rootSaga).done.then(() => {
res.status(200).send(
layout(
renderToString(rootComp),
JSON.stringify(store.getState())
)
)
});
renderToString(rootComp);
store.close()
store.runSaga = sagaMiddleware.run;
store.close = () => store.dispatch(END);

3. Use channels for dependent async requests

function* fetchSomethingSaga() {
const ourChannel = yield call(channel);
// create channel factory to queue incoming requests
yield fork(somethingElseHandler, ourChannel);
// create a worker thread for forked saga and supply channel
try {
while(true) { // loop so watch works more than once
yield take('FETCH_SOMETHING'); // watching for action
const items = yield call(fetchItems); // 1st async call

yield put(ourChannel, fetchSomethingElseAction(items));
// 2nd async call via action and send payload into channel
}
} finally { // END triggered
ourChannel.close(); // close + unsubscribe from channel
}
}
export function* somethingElseHandler(channel) {
while(true) {
const action = yield take(channel)
// observe the handed channel factory
yield call(fetchSomethingElse, action)
// make async request with the channel factory
}
}

4. Utilise lazy-loading

Main page contentExtra content relating to main content
componentWillMount() {
if (!this.props.someValueFromStore) { // via mapStateToProps
this.props.loadValue(this.props.x); // via mapDispatchToProps
}
}
function * fetchMainContent({payload}) {
const { itemId } = payload;
yield put(loadMainContent(itemId));
const mainData = yield call(fetchContent(itemId));
yield put(loadMainContentSuccess(itemId));
// load extra content
yield put(loadExtraContent(mainData.extraId));
const extraData = yield call(fetchExtraValue(mainData.extraId));
yield put(loadExtraContentSuccess(mainData.extraId));
}
componentWillMount() {
if (!this.props.someValueFromStore) {
this.props.loadValue(this.props.x);
} else {
this.props.loadExtraValue(this.props.someValueFromStore);
}
}

5. Manage components rendered on the server

{ 
(process.browser) &&
<Media query={ X }>...</Media>
}

6. Defer the bundle

<script key={ X } src={ X } />
<script defer={ 'defer' } key={ X } src={ X } />

7. Profiling

7.1 Manually:

  • TTI: how long for the user to be able to interact with the page
  • OnLoad: how long for the entire page to be completely ready
  • Network Fast 3G
  • CPU: 4x slowdown
TTI = performance.timing.domInteractive - performance.timing.navigationStartOnLoad = performance.timing.loadEventEnd - performance.timing.navigationStart

7.2 WebPageTest:

  1. Chrome desktop with fast local internet
  2. Android Moto G4 with average 3G
  3. iPhone SE with average 3G

7.3 Google Chrome Lighthouse:

8. Results:

--

--

--

JS "under-the-hood of" series https://bit.ly/36GLhlo. dev @ fiit.tv. Formerly BBC. https://craigtaub.dev/

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

13 JavaScript Array Methods

Final Project Pt.2

Automated Content Migration to WordPress

Summery about 6 Days of JavaScript

ESM in Node has been unflagged! + other Node.js updates of this week. #46 / 2019

What is “this” in Javascript

https://concept-tees.myspreadshop.com/barz+rising+mcs+represesentation-A5c6f7ec81cbf3a07eb73e669

Should you use the official Stripe package in your React Native app

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Craig Taub

Craig Taub

JS "under-the-hood of" series https://bit.ly/36GLhlo. dev @ fiit.tv. Formerly BBC. https://craigtaub.dev/

More from Medium

Component Rendering in Reactjs(List rendering and conditional rendering)

Clean Code UI Refactoring at Axon 🎉

What’s new in React 18?!

Unit test in React