Created
June 4, 2016 18:38
-
-
Save olalonde/f3dc38c8c424c3a96d7a699c57f981fd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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