From af644ef1fb13eb0d83a853a2ee321515e8c91408 Mon Sep 17 00:00:00 2001 From: sergesemashko <semashkosergey@gmail.com> Date: Sat, 30 Jan 2016 14:48:46 -0500 Subject: [PATCH] Proof of concept of deffered props --- modules/ReduxAsyncConnect.js | 32 +++++++++++++++++++++++++++++--- modules/asyncConnect.js | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/modules/ReduxAsyncConnect.js b/modules/ReduxAsyncConnect.js index d1b684e1..1296bba0 100644 --- a/modules/ReduxAsyncConnect.js +++ b/modules/ReduxAsyncConnect.js @@ -35,11 +35,19 @@ function filterAndFlattenComponents(components) { return flattened; } -function asyncConnectPromises(components, params, store, helpers) { - return components.map(Component => Component.reduxAsyncConnect(params, store, helpers)) + +function asyncConnectDeferredPromises(components, params, store, helpers) { + return components.map(Component => Component.reduxAsyncConnectDeferred(params, store, helpers)) .filter(result => result && result.then instanceof Function); } +function asyncConnectPromises(components, params, store, helpers) { + return [].concat(components.map(Component => Component.reduxAsyncConnect(params, store, helpers)) + .filter(result => result && result.then instanceof Function), + asyncConnectDeferredPromises(components, params, store, helpers) + ); +} + export function loadOnServer({ components, params }, store, helpers) { return Promise.all(asyncConnectPromises(filterAndFlattenComponents(components), params, store, helpers)) .catch(error => console.error('reduxAsyncConnect server promise error: ' + error)).then(() => { @@ -84,7 +92,10 @@ class ReduxAsyncConnect extends React.Component { componentDidMount() { const dataLoaded = this.isLoaded(); - if (!dataLoaded) { // we dont need it if we already made it on server-side + if (dataLoaded) { + // load deferred data if we already made initial load on server-side + this.loadDeferredData(this.props); + } else { this.loadAsyncData(this.props); } } @@ -97,6 +108,21 @@ class ReduxAsyncConnect extends React.Component { return this.state.propsToShow !== nextState.propsToShow; } + loadDeferredData(props) { + const { components, params, helpers } = props; + const store = this.context.store; + const promises = asyncConnectDeferredPromises(filterAndFlattenComponents(components), params, store, helpers); + + if (promises.length) { + Promise.all(promises).catch(error => console.error('reduxAsyncConnect server promise error: ' + error)) + .then(() => { + this.setState({propsToShow: props}); + }); + } else { + this.setState({propsToShow: props}); + } + } + loadAsyncData(props) { const { components, params, helpers } = props; const store = this.context.store; diff --git a/modules/asyncConnect.js b/modules/asyncConnect.js index a6b86b96..31c9e75b 100644 --- a/modules/asyncConnect.js +++ b/modules/asyncConnect.js @@ -1,5 +1,9 @@ import { connect } from 'react-redux'; +// use Function constructor to obtain a proper global object through `this` +const isNode = (new Function("try {return this===global;}catch(e){return false;}"))(); + +export const INIT_DEFERRED = 'reduxAsyncConnect/INIT_DEFERRED'; export const LOAD = 'reduxAsyncConnect/LOAD'; export const LOAD_SUCCESS = 'reduxAsyncConnect/LOAD_SUCCESS'; export const LOAD_FAIL = 'reduxAsyncConnect/LOAD_FAIL'; @@ -7,6 +11,11 @@ export const CLEAR = 'reduxAsyncConnect/CLEAR'; export const BEGIN_GLOBAL_LOAD = 'reduxAsyncConnect/BEGIN_GLOBAL_LOAD'; export const END_GLOBAL_LOAD = 'reduxAsyncConnect/END_GLOBAL_LOAD'; +const INITIAL_DEFERRED_STATE = { + loading: false, + loaded: false +}; + export function reducer(state = {loaded: false}, action = {}) { const stateSlice = state[action.key]; @@ -59,6 +68,11 @@ export function reducer(state = {loaded: false}, action = {}) { loading: false } }; + case INIT_DEFERRED: + return { + ...state, + [action.key]: INITIAL_DEFERRED_STATE + }; default: return state; } @@ -102,6 +116,13 @@ function loadFail(key, error) { }; } +function initDeferred(key) { + return { + type: INIT_DEFERRED, + key + }; +} + function componentLoadCb(mapStateToProps, params, store, helpers) { const dispatch = store.dispatch; @@ -126,12 +147,21 @@ function componentLoadCb(mapStateToProps, params, store, helpers) { return promises.length === 0 ? null : Promise.all(promises); } -export function asyncConnect(mapStateToProps) { +export function asyncConnect(mapStateToProps = {}, deferredProps = {}) { return Component => { - Component.reduxAsyncConnect = (params, store, helpers) => componentLoadCb(mapStateToProps, params, store, helpers); + + Component.reduxAsyncConnect = (params, store, helpers) => componentLoadCb(mapStateToProps, params, store, helpers) + Component.reduxAsyncConnectDeferred = (params, store, helpers) => { + const dispatch = store.dispatch; + return isNode ? + // Skip loading deferred props on server-side + Object.keys(deferredProps).map(key => dispatch(initDeferred(key))) : + // Load deferred props if not server + componentLoadCb(deferredProps, params, store, helpers); + } const finalMapStateToProps = state => { - return Object.keys(mapStateToProps).reduce((result, key) => ({...result, [key]: state.reduxAsyncConnect[key]}), {}); + return Object.keys({...mapStateToProps, ...deferredProps}).reduce((result, key) => ({...result, [key]: state.reduxAsyncConnect[key]}), {}); }; return connect(finalMapStateToProps)(Component);