diff --git a/README.md b/README.md index d551d1945..f86d31a75 100755 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ This is a starter boilerplate app I've put together using the following technolo * [Redux](https://github.com/rackt/redux)'s futuristic [Flux](https://facebook.github.io/react/blog/2014/05/06/flux.html) implementation * [Redux Dev Tools](https://github.com/gaearon/redux-devtools) for next generation DX (developer experience). Watch [Dan Abramov's talk](https://www.youtube.com/watch?v=xsSnOQynTHs). * [Redux Router](https://github.com/rackt/redux-router) Keep your router state in your Redux store +* [react-fetcher](https://github.com/markdalgleish/react-fetcher) for route-based universal data fetching * [ESLint](http://eslint.org) to maintain a consistent code style * [redux-form](https://github.com/erikras/redux-form) to manage form state in Redux * [lru-memoize](https://github.com/erikras/lru-memoize) to speed up form validation @@ -86,7 +87,7 @@ We also spit out the `redux` state into a global `window.__data` variable in the #### Server-side Data Fetching -We ask `react-router` for a list of all the routes that match the current request and we check to see if any of the matched routes has a static `fetchData()` function. If it does, we pass the redux dispatcher to it and collect the promises returned. Those promises will be resolved when each matching route has loaded its necessary data from the API server. +We ask `react-router` for a list of all the routes that match the current request and pass them to `react-fetcher` to check to see if any of the matched routes have a `@prefetch` or `@defer` decorator function. If it does, we pass the redux dispatcher to it and `react-fetcher` collects the promises returned. Those promises will be resolved when each matching route has loaded its necessary data from the API server. #### Client Side diff --git a/package.json b/package.json index ba9c6c328..322786463 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,6 @@ "express-session": "^1.12.1", "file-loader": "^0.8.4", "history": "^1.13.0", - "hoist-non-react-statics": "^1.0.3", "http-proxy": "^1.12.0", "less": "^2.5.3", "less-loader": "^2.2.1", @@ -103,6 +102,7 @@ "react-bootstrap": "^0.27.3", "react-document-meta": "^2.0.0", "react-dom": "^0.14.1", + "react-fetcher": "^0.1.0", "react-inline-css": "^2.0.0", "react-redux": "^4.0.0", "react-router": "^1.0.0-rc3", diff --git a/src/containers/App/App.js b/src/containers/App/App.js index c67790bc6..b34256ef7 100755 --- a/src/containers/App/App.js +++ b/src/containers/App/App.js @@ -8,10 +8,10 @@ import { isLoaded as isInfoLoaded, load as loadInfo } from 'redux/modules/info'; import { isLoaded as isAuthLoaded, load as loadAuth, logout } from 'redux/modules/auth'; import { InfoBar } from 'components'; import { pushState } from 'redux-router'; -import connectData from 'helpers/connectData'; +import { prefetch } from 'react-fetcher'; import config from '../../config'; -function fetchData(getState, dispatch) { +@prefetch(({ getState, dispatch }) => { const promises = []; if (!isInfoLoaded(getState())) { promises.push(dispatch(loadInfo())); @@ -20,9 +20,7 @@ function fetchData(getState, dispatch) { promises.push(dispatch(loadAuth())); } return Promise.all(promises); -} - -@connectData(fetchData) +}) @connect( state => ({user: state.auth.user}), {logout, pushState}) diff --git a/src/containers/Home/Home.js b/src/containers/Home/Home.js index 44499b7dd..4c03c233d 100755 --- a/src/containers/Home/Home.js +++ b/src/containers/Home/Home.js @@ -79,6 +79,7 @@ export default class Home extends Component {
Widgets.js's
- fetchData() function is called before the widgets page is loaded, on either the server
- or the client, allowing all the widget data to be loaded and ready for the page to render.
+ some source that is needed to complete the server-side rendering.
+ Widgets.js's @defer decorator is passed a function that is called
+ by react-fetcher
+ before the widgets page is loaded, on either the server or the client, allowing all the widget
+ data to be loaded and ready for the page to render.
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 uses the @defer decorator
+ from react-fetcher. To block a
+ route transition until some data is loaded, use @prefetch. 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__/connectData-test.js b/src/helpers/__tests__/connectData-test.js deleted file mode 100644 index d61d73bf4..000000000 --- a/src/helpers/__tests__/connectData-test.js +++ /dev/null @@ -1,29 +0,0 @@ -import { expect } from 'chai'; -import React from 'react'; -import { div } from 'react-dom'; -import connectData from '../connectData'; - -describe('connectData', () => { - let fetchData; - let fetchDataDeferred; - let WrappedComponent; - let DataComponent; - - beforeEach(() => { - fetchData = 'fetchDataFunction'; - fetchDataDeferred = 'fetchDataDeferredFunction'; - - WrappedComponent = () => -
; - - DataComponent = connectData(fetchData, fetchDataDeferred)(WrappedComponent); - }); - - it('should set fetchData as a static property of the final component', () => { - expect(DataComponent.fetchData).to.equal(fetchData); - }); - - it('should set fetchDataDeferred as a static property of the final component', () => { - expect(DataComponent.fetchDataDeferred).to.equal(fetchDataDeferred); - }); -}); diff --git a/src/helpers/__tests__/getDataDependencies-test.js b/src/helpers/__tests__/getDataDependencies-test.js deleted file mode 100644 index 70ee2b2c1..000000000 --- a/src/helpers/__tests__/getDataDependencies-test.js +++ /dev/null @@ -1,61 +0,0 @@ -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; - - 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}`; - }; - }); - - it('should get fetchDatas', () => { - const deps = getDataDependencies([ - CompWithFetchData, - CompWithNoData, - CompWithFetchDataDeferred - ], getState, dispatch, location, params); - - expect(deps).to.deep.equal([ - 'fetchData getState dispatch location params' - ]); - }); - - it('should get fetchDataDeferreds', () => { - const deps = getDataDependencies([ - CompWithFetchData, - CompWithNoData, - CompWithFetchDataDeferred - ], getState, dispatch, location, params, true); - - expect(deps).to.deep.equal([ - 'fetchDataDeferred getState dispatch location params' - ]); - }); -}); diff --git a/src/helpers/connectData.js b/src/helpers/connectData.js deleted file mode 100644 index 4ad672720..000000000 --- a/src/helpers/connectData.js +++ /dev/null @@ -1,24 +0,0 @@ -import React, { Component } from 'react'; -import hoistStatics from 'hoist-non-react-statics'; - -/* - Note: - When this decorator is used, it MUST be the first (outermost) decorator. - Otherwise, we cannot find and call the fetchData and fetchDataDeffered methods. -*/ - -export default function connectData(fetchData, fetchDataDeferred) { - - return function wrapWithFetchData(WrappedComponent) { - class ConnectData extends Component { - render() { - return