Last active
February 7, 2018 23:39
-
-
Save gilbert/8d17d675950265a188f846cbf0bf7c23 to your computer and use it in GitHub Desktop.
React GET Data Dependencies helper
This file contains hidden or 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
// | |
// Our main data fetching tool. | |
// This fetches an object of promise makers ONLY ON THE CLIENT SIDE, | |
// and manages loading and data states for each key. | |
// | |
import React from 'react' | |
import http from './http' | |
export default (promisesMakers, options={}) => Component => | |
class FetchHOC extends React.Component { | |
constructor(props) { | |
super(props) | |
this._isMounted = false | |
this.state = { | |
loading: true, // true if any dep is loading | |
error: null, // populated with first dep error if any | |
} | |
for (let key in promisesMakers) { | |
// | |
// Initialize with loading to ensure child component doesn't try to use data | |
// | |
this.state[key+'Loading'] = true | |
} | |
} | |
componentWillMount() { | |
// Only fetch data in the browser | |
if ( ! process.browser ) return; | |
this._isMounted = true | |
for (let key in promisesMakers) { | |
let fetch = () => { | |
if ( this._isMounted !== true || this.state[key+'Loading'] === true ) return; | |
this.state[key+'Loading'] = true | |
this.state[key+'Error'] = null | |
var fetcher = promisesMakers[key] | |
if ( typeof fetcher === 'string' ) { | |
let fetchUrl = fetcher | |
fetcher = () => fetchUrl | |
} | |
if ( ! (typeof fetcher === 'function') ) { | |
throw new Error(`[WithData] Value of ${key} is not a function.`) | |
} | |
var promise = fetcher(this.props) | |
if ( typeof promise === 'string' ) { | |
// Function returned a url to GET | |
promise = http.get(promise).then( res => res.data ) | |
} | |
this.state[key+'Promise'] = promise | |
return promise.then( | |
data => { | |
this.state[key+'Loading'] = false | |
this.state[key] = data | |
this.calcOveralLoadingStatus() | |
}, | |
error => { | |
console.log('[DataDeps] Error', key, error) | |
this.state[key+'Loading'] = false | |
if ( ! this.state.error ) { | |
this.setState({ error: error }) | |
} | |
if ( error.response && error.response.status === 404 ) { | |
this.state[key+'Error'] = '404_not_found' | |
} | |
else { | |
this.state[key+'Error'] = `Unknown error: ${JSON.stringify(error)}` | |
} | |
this.calcOveralLoadingStatus() | |
} | |
) | |
} | |
this.state[key] = null | |
this.state[key+'Refetech'] = fetch | |
// Temporarily set to false so `fetch` can run | |
this.state[key+'Loading'] = false | |
fetch() | |
} | |
this.forceUpdate() | |
} | |
calcOveralLoadingStatus() { | |
var loading = false | |
for (var key in promisesMakers) { | |
loading = loading || this.state[key+'Loading'] | |
} | |
this.setState({ loading: loading }) | |
} | |
componentWillUnmount() { | |
this._isMounted = false | |
} | |
render() { | |
var component = <Component {...this.props} {...this.state} /> | |
if ( options.preload ) { | |
var preloadView = options.preload === true | |
? <div className="loading-lg"></div> | |
: options.preload | |
return this.state.loading ? preloadView : component | |
} | |
else { | |
return component | |
} | |
} | |
} |
This file contains hidden or 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 dataDeps from '...' | |
var withData = dataDeps({ | |
contact: (props) => `/api/me/contact/${props.id}`, | |
contactFields: '/api/me/contact/fields', | |
}) | |
export default withData((props) => { | |
if (props.loading) { | |
// Also available: props.contactLoading, props.contactFieldsLoading | |
return <div className="loading"></div> | |
} | |
if (props.error) { | |
// props.error is the first failed dep, if any | |
// Also available: props.contactError, props.contactFieldsError | |
return <div>Error: {props.error.message}</div> | |
} | |
var {contact, contactFields} = this.props | |
return <div> | |
Contact: {contact.name} | |
... | |
</div> | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment