Skip to content

Instantly share code, notes, and snippets.

@dlebedynskyi
Last active February 15, 2017 16:11
Show Gist options
  • Save dlebedynskyi/575cc138c57af6b47f7c3f917b1f57f4 to your computer and use it in GitHub Desktop.
Save dlebedynskyi/575cc138c57af6b47f7c3f917b1f57f4 to your computer and use it in GitHub Desktop.
Prefetch
const hoc = compose(
connect(mapStateToProps, mapDispatchToProps),
prefetch({
fetch: ({pathname, load, hasLoaded}) =>
(hasLoaded ? Promise.resolve() : load(pathname)),
defer: ({
doSecureClientAPICallOne,
doSecureClientAPICallTwo
}) => [doSecureClientAPICall(), doSecureClientAPICallTwo()]
}),
pure);
const Test = ({somedata, hasLoaded, otherDataFromDefer}) => (...)
export default hoc(Test);
import React, {Component, PropTypes} from 'react';
/**
* High order Component to request data load before rendering
* Has 2 props as params - fetch and defer.
* fetch - will be called both on client and server.
* It is your responsibility to make only server call or both.
* defer - will be called only on client side.
* @param {Promise|array[Promise]} fetch - promise(s) to resolve on server and client
* @param {Promise|array[Promise]} defer - promise(s) to resolve on client
*/
const hoc = ({fetch, defer}) => WrappedCompontent =>
class Prefetch extends Component {
static contextTypes = {
store: PropTypes.object, // Redux store.
serverFetchPromises: PropTypes.array
};
displayName = 'Prefetch';
constructor(props, context) {
super(props, context);
const {serverFetchPromises, store} = context;
// only server should set serverFetchPromises
// if this one exists then we fetch will called twice
if (serverFetchPromises && typeof fetch === 'function') {
const all = [].concat(fetch(props, store));
all.forEach(p => p && serverFetchPromises.push(p));
}
}
componentDidMount() {
const {serverFetchPromises, store} = this.context;
const promises = serverFetchPromises || [];
// making sure promises are resolved
// so call fetch again on client side, since server may fail some
if (typeof fetch === 'function') {
const all = [].concat(fetch(this.props, store));
all.forEach(p => p && promises.push(p));
}
// only client call data will happen.
// still going to add to general collection for consistency
if (typeof defer === 'function') {
const all = [].concat(defer(this.props, store));
all.forEach(p => p && promises.push(p));
}
}
render() {
return (<WrappedCompontent {...this.props}/>);
}
};
hoc.propTypes = {
fetch: React.PropTypes.func,
defer: React.PropTypes.func
};
export default hoc;
// server render method
const promises = [];
<Provider store={store}>
<ServerFetchProvider promises={promises}>
{/* rest of stuff*/}
</ServerFetchProvider>
</Provider>
if (promises.length > 0) {
await awaitForAll(fetchPromises);
}
import React from 'react';
/**
* Server only Provider component that passes promises as context
*/
export default class ServerFetchProvider extends React.Component {
static childContextTypes = {
serverFetchPromises: React.PropTypes.array
};
static propTypes = {
children: React.PropTypes.node,
promises: React.PropTypes.array.isRequired
};
getChildContext() {
return {
serverFetchPromises: this.props.promises
};
}
render() {
return React.Children.only(this.props.children);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment