From 9d96a8e2f236b94b3549f78dd076430f3525006f Mon Sep 17 00:00:00 2001 From: stevo Date: Wed, 14 Oct 2015 10:41:35 +0100 Subject: [PATCH] Support fetchDataDeferred Loads data after route transition on the client --- src/containers/Widgets/Widgets.js | 9 ++- .../__tests__/getDataDependencies-test.js | 78 +++++++++++++++++++ src/helpers/getDataDependencies.js | 14 ++-- src/redux/middleware/transitionMiddleware.js | 16 ++-- 4 files changed, 102 insertions(+), 15 deletions(-) create mode 100644 src/helpers/__tests__/getDataDependencies-test.js diff --git a/src/containers/Widgets/Widgets.js b/src/containers/Widgets/Widgets.js index 79484cce8..a6fa0230f 100755 --- a/src/containers/Widgets/Widgets.js +++ b/src/containers/Widgets/Widgets.js @@ -26,7 +26,7 @@ class Widgets extends Component { editStart: PropTypes.func.isRequired } - static fetchData(getState, dispatch) { + static fetchDataDeferred(getState, dispatch) { if (!isLoaded(getState())) { return dispatch(loadWidgets()); } @@ -56,9 +56,10 @@ class Widgets extends Component {

- This data was loaded from the server before this route was rendered. If you hit refresh on your browser, the - data loading will take place on the server before the page is returned. If you navigated here from another - page, the data was fetched from the client. + If you hit refresh on your browser, the data loading will take place on the server before the page is returned. + If you navigated here from another page, the data was fetched from the client after the route transition. + This uses the static method fetchDataDeferred. To block a route transition until some data is loaded, use fetchData. + To always render before loading data, even on the server, use componentDidMount.

This widgets are stored in your session, so feel free to edit it and refresh. diff --git a/src/helpers/__tests__/getDataDependencies-test.js b/src/helpers/__tests__/getDataDependencies-test.js new file mode 100644 index 000000000..08ea664d1 --- /dev/null +++ b/src/helpers/__tests__/getDataDependencies-test.js @@ -0,0 +1,78 @@ +import { expect } from 'chai'; +import React from 'react'; +import { div } from 'react-dom'; +import getDataDependencies from '../getDataDependencies'; + +describe('getDataDependencies', () => { + let getState; + let dispatch; + let location; + let params; + let CompWithFetchData; + let CompWithNoData; + let CompWithFetchDataDeferred; + let ConnectedCompWithFetchData; + let ConnectedCompWithFetchDataDeferred; + + beforeEach(() => { + getState = 'getState'; + dispatch = 'dispatch'; + location = 'location'; + params = 'params'; + + CompWithNoData = () => +

; + + CompWithFetchData = () => +
; + + CompWithFetchData.fetchData = (_getState, _dispatch, _location, _params) => { + return `fetchData ${_getState} ${_dispatch} ${_location} ${_params}`; + }; + CompWithFetchDataDeferred = () => +
; + + CompWithFetchDataDeferred.fetchDataDeferred = (_getState, _dispatch, _location, _params) => { + return `fetchDataDeferred ${_getState} ${_dispatch} ${_location} ${_params}`; + }; + + ConnectedCompWithFetchData = () => +
; + + ConnectedCompWithFetchData.WrappedComponent = CompWithFetchData; + + ConnectedCompWithFetchDataDeferred = () => +
; + + ConnectedCompWithFetchDataDeferred.WrappedComponent = CompWithFetchDataDeferred; + }); + + it('should get fetchDatas', () => { + const deps = getDataDependencies([ + CompWithFetchData, + CompWithNoData, + CompWithFetchDataDeferred, + ConnectedCompWithFetchData, + ConnectedCompWithFetchDataDeferred + ], getState, dispatch, location, params); + + expect(deps).to.deep.equal([ + 'fetchData getState dispatch location params', + 'fetchData getState dispatch location params' + ]); + }); + + it('should get fetchDataDeferreds', () => { + const deps = getDataDependencies([ + CompWithFetchData, + CompWithNoData, + CompWithFetchDataDeferred, + ConnectedCompWithFetchDataDeferred + ], getState, dispatch, location, params, true); + + expect(deps).to.deep.equal([ + 'fetchDataDeferred getState dispatch location params', + 'fetchDataDeferred getState dispatch location params' + ]); + }); +}); diff --git a/src/helpers/getDataDependencies.js b/src/helpers/getDataDependencies.js index 5c742fe00..44af9aa7e 100755 --- a/src/helpers/getDataDependencies.js +++ b/src/helpers/getDataDependencies.js @@ -1,13 +1,15 @@ -const getDataDependency = (component = {}) => { +const getDataDependency = (component = {}, methodName) => { return component.WrappedComponent ? - getDataDependency(component.WrappedComponent) : - component.fetchData; + getDataDependency(component.WrappedComponent, methodName) : + component[methodName]; }; -export default (components, getState, dispatch, location, params) => { +export default (components, getState, dispatch, location, params, deferred) => { + const methodName = deferred ? 'fetchDataDeferred' : 'fetchData'; + return components - .filter((component) => getDataDependency(component)) // only look at ones with a static fetchData() - .map(getDataDependency) // pull out fetch data methods + .filter((component) => getDataDependency(component, methodName)) // only look at ones with a static fetchData() + .map((component) => getDataDependency(component, methodName)) // pull out fetch data methods .map(fetchData => fetchData(getState, dispatch, location, params)); // call fetch data methods and save promises }; diff --git a/src/redux/middleware/transitionMiddleware.js b/src/redux/middleware/transitionMiddleware.js index 1707ab76a..c58d7e9da 100644 --- a/src/redux/middleware/transitionMiddleware.js +++ b/src/redux/middleware/transitionMiddleware.js @@ -20,11 +20,17 @@ export default ({getState, dispatch}) => next => action => { next(action); } else { const {components, location, params} = action.payload; - const promises = getDataDependencies(components, getState, dispatch, location, params); - const promise = Promise.all(promises) - .then( - () => next(action) - ); + const promise = new Promise((resolve) => { + + const doTransition = () => { + next(action); + Promise.all(getDataDependencies(components, getState, dispatch, location, params, true)) + .then(resolve, resolve); + }; + + Promise.all(getDataDependencies(components, getState, dispatch, location, params)) + .then(doTransition, doTransition); + }); if (__SERVER__) { // router state is null until ReduxRouter is created so we can use this to store