Skip to content

Instantly share code, notes, and snippets.

@olalonde
Created June 4, 2016 18:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save olalonde/f3dc38c8c424c3a96d7a699c57f981fd to your computer and use it in GitHub Desktop.
Save olalonde/f3dc38c8c424c3a96d7a699c57f981fd to your computer and use it in GitHub Desktop.
/**
* This module is used to implement server side rendering
* with async http requests
*/
import ReactDOM from 'react-dom/server'
// TODO
// export default waitKey = '@@wait'
const { PENDING, RESOLVED, LOADED } = {
PENDING: '@@wait/PENDING',
RESOLVED: '@@wait/RESOLVED',
LOADED: '@@wait/LOADED',
}
// Transparently wraps "thunk action creators"
// and increments a wait count in the store until
// the thunk and then decrement the count
export default (thunk) => (dispatch, getState) => {
const wait = getState().wait
// After a first render pass, we can ignore wait() calls
if (wait.loaded) return dispatch(thunk)
dispatch({ type: PENDING })
return dispatch(thunk).then((res) => {
// console.log('resolved')
dispatch({ type: RESOLVED })
return res
}).catch((err) => {
dispatch({ type: RESOLVED })
throw err
})
}
export const waitReducer = (state = {
loaded: false,
count: 0,
}, { type }) => {
if (type === PENDING) {
return {
...state,
count: state.count + 1,
}
} else if (type === RESOLVED) {
return {
...state,
count: state.count - 1,
}
} else if (type === LOADED) {
return {
...state,
loaded: true,
}
}
return state
}
// TODO: add timeout?
/* eslint-disable prefer-const */
export const populateStore = (store) => (component) => {
let lastAsyncCount = 0
const renderAndWait = () => new Promise((resolve) => {
let done = false
let unsubscribe
let asyncCount = 0
const maybeDone = () => {
if (done) return
const wait = store.getState().wait
asyncCount += wait.count
/*
const state = store.getState()
console.log('wait = ', state.wait)
console.log('user = ', state.user && state.user.email)
console.log('blog = ', state.blog)
*/
if (wait.count === 0) {
done = true
unsubscribe()
resolve(asyncCount)
}
}
unsubscribe = store.subscribe(maybeDone)
// triggers all the render and lifecycle method
// which might dispatch async action creators
// console.log('ReactDOM.renderToString()')
ReactDOM.renderToString(component)
// trigger maybeDone just in case nothing was dispatched
maybeDone()
}).then((asyncCount) => {
if (asyncCount > lastAsyncCount) {
lastAsyncCount = asyncCount
// we needlessly need to call all the
// componentWillMount that we previously
// triggered and wait for the async actions
// twice or more... :(
return renderAndWait()
}
return asyncCount
})
return renderAndWait().then(() => {
// console.log('LOADED!')
store.dispatch({ type: LOADED })
// console.log(store.getState().wait)
return null
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment