Created
August 22, 2016 18:17
-
-
Save geophree/bb438d97b243d4a8c185ede91075d5ea to your computer and use it in GitHub Desktop.
Universal redux-saga loading module WIP
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'; | |
export const LOADING_START = 'app-resource/loading/LOADING_START'; | |
export const LOADING_DONE = 'app-resource/loading/LOADING_DONE'; | |
export const LOADING_ERROR = 'app-resource/loading/LOADING_ERROR'; | |
const defaultState = Object.freeze({ | |
loading: [], | |
error: [], | |
}); | |
export default function loadingReducer(state = defaultState, action) { | |
const { type, data, err } = action; | |
switch (type) { | |
case LOADING_START: | |
console.log('loading start'); | |
return Object.assign({}, state, {loading: R.append(data, state.loading)}); | |
case LOADING_DONE: | |
console.log('loading done'); | |
const without = R.without([data]); | |
const oneLess = R.compose(R.tail, R.filter(R.equals(data))); | |
const removeOne = R.converge(R.concat, [without, oneLess]); | |
return Object.assign({}, state, {loading: removeOne(state.loading)}); | |
case LOADING_ERROR: | |
console.log('loading error'); | |
return Object.assign({}, state, {error: R.append([data, err], state.error)}); | |
default: | |
return state; | |
} | |
} | |
export const loadingStart = (data) => ({type: LOADING_START, data}); | |
export const loadingDone = (data) => ({type: LOADING_DONE, data}); | |
export const loadingError = (data, err) => ({type: LOADING_ERROR, data, err}); | |
export const isLoadingSelector = ({loading: {loading}}) => loading.length !== 0; | |
export const errorSelector = ({loading: {error}}) => error; |
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 {LOADING_DONE, loadingStart, loadingDone, loadingError, isLoadingSelector, errorSelector} from './Loading'; | |
import {call, put, select, take} from 'redux-saga/effects'; | |
export const addLoading = (func) => { | |
return function* addLoadingSaga(...args) { | |
console.log('add loading saga'); | |
const type = 'TBD'; | |
const data = [type, func, ...args]; | |
yield put(loadingStart(data)); | |
console.log('loading started'); | |
try { | |
yield call(func, ...args); | |
console.log('loaded call done'); | |
} catch (err) { | |
yield put(loadingError(data, err)); | |
console.log('loading errored '); | |
} finally { | |
yield put(loadingDone(data)); | |
console.log('loading done'); | |
} | |
}; | |
}; | |
export function* waitForLoadingComplete() { | |
console.log('A1'); | |
while (yield select(isLoadingSelector)) { | |
console.log('A2'); | |
console.log(yield select((state) => state.loading)); | |
yield take(LOADING_DONE); | |
console.log('A3'); | |
} | |
const err = yield select(errorSelector); | |
if (err.length) { | |
console.log('A4'); | |
throw err[0]; | |
} | |
console.log('A5'); | |
} | |
export function* renderUntilDone() { | |
let again = true; | |
let renderCount = 0; | |
let html; | |
yield take(LOADING_DONE); | |
yield onChange(true); | |
function* onChange(first = false) { | |
const isLoading = yield select(isLoadingSelector); | |
if (!first && isLoading) { | |
again = true; | |
return; | |
} | |
if (again) { | |
again = false; | |
render(); | |
} | |
// This may not work if onChange isn't called sync during render | |
if (!again) { | |
unsub(); | |
return 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)); | |
} | |
} | |
// TODO(@geophree): Add an makeLoadingActionCreator, and takeEveryLoading | |
// TODO(@geophree): what we really want is a way to wait until all actions have resolved through the sagas, | |
// and all finite sagas (sagas we expect to complete vs infinite-loop sagas) have completed. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment