Last active
August 22, 2016 18:08
-
-
Save geophree/522942ae19bd105743a35cff48feab28 to your computer and use it in GitHub Desktop.
Universal redux-saga server-side render
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
import R from 'ramda'; | |
import ReactDOMServer from 'react-dom/server'; | |
import { put, call } from 'redux-saga/effects'; | |
const LOADING = 'LOADING'; | |
const MAX_RENDERS = 10; | |
export function renderUntilDone(store, app) { | |
return new Promise((resolve, reject) => { | |
let again = true; | |
let renderCount = 0; | |
let html; | |
const unsub = store.subscribe(onChange); | |
onChange(true); | |
function onChange(first = false) { | |
const loading = store.getState().loading; | |
if (!first && loading && loading.length) { | |
again = true; | |
return; | |
} | |
if (again) { | |
again = false; | |
render(); | |
} | |
// This may not work if onChange isn't called sync during render | |
if (!again) { | |
unsub(); | |
resolve(html); | |
} | |
} | |
function render() { | |
renderCount++; | |
if (renderCount > MAX_RENDERS) { | |
unsub(); | |
return reject(new Error(`Tried to render more than ${MAX_RENDERS} times.`)); | |
} | |
const state = store.getState(); | |
html = ReactDOMServer.renderToString(app(state)); | |
} | |
}); | |
} | |
export function reducer(state = [], action) { | |
if (action.type !== LOADING) return state; | |
// no-dupes version | |
// const mod = action.finished ? R.without([action.data]) : R.compose(R.uniq, R.append(action.data)); | |
const without = R.without([action.data]); | |
const oneLess = R.compose(R.tail, R.filter(R.equals(action.data))); | |
const removeOne = R.converge(R.concat, [without, oneLess]); | |
const mod = action.finished ? removeOne : R.append(action.data); | |
return mod(loading); | |
} | |
// Wrap your loading sagas with this function | |
export function *addLoading(type, func, ...args) { | |
const data = [type, func, ...args]; | |
yield put({ type: LOADING, data }); | |
try { | |
yield call(func, ...args); | |
} finally { | |
yield put({ type: LOADING, data, finished: true }); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment