Created
April 20, 2017 09:10
-
-
Save marg51/653a558a3b77450e9e4dca84db8724b5 to your computer and use it in GitHub Desktop.
React — Decorators and how to easily manage loading states
This file contains 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 React from "react" | |
export const promiseable = (self, fnName, attrs) => { | |
const name = null // not sure how to pass that from outside | |
const fn = self[fnName] // function being decorated | |
// we replace the function with a new one | |
self[fnName] = function() { | |
this.setState(createObject("loading", name)) | |
// we call the previous function. | |
const promise = fn.call(this) | |
if(!promise || !promise.then) { | |
return this.setState(createObject("failed", name, "you should return a promise")) | |
} | |
return promise.then(data => { | |
this.setState(createObject("loaded", name, data)) | |
return data | |
}, data => { | |
this.setState(createObject("failed", name, data)) | |
return data | |
}) | |
} | |
return self | |
} | |
export const renderWhenLoaded = (self, fnName) => { | |
const fn = self[fnName] | |
self[fnName] = function() { | |
if(this.state.isLoading) | |
return <div>Loading</div> | |
if(this.state.hasError) { | |
console.error(this.state.error) | |
return <div>Error! {JSON.stringify(this.state.error)}</div> | |
} | |
return fn.call(this) | |
} | |
return self | |
} | |
function createObject(status, name, data) { | |
let object | |
switch(status) { | |
case "loading": | |
object = {isLoading: true, isLoaded: false, hasError: false} | |
break | |
case "loaded": | |
object = {isLoading: false, isLoaded: true, hasError: false} | |
break | |
case "failed": | |
object = {isLoading: false, isLoaded: false, hasError: true, error: data} | |
break | |
default: | |
throw new Error(`[promise]: status ${status} is not valid. Should be one of loading | loaded | failed`) | |
} | |
if(name) { | |
object = {[name]: object} | |
} | |
return object | |
} |
This file contains 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 React, {Component} from 'react'; | |
import {promiseable, renderWhenLoaded} from "./promise" | |
export default class View extends Component { | |
constructor() { | |
super() | |
this.state = { | |
data: {a: 1} | |
} | |
} | |
loadData() { | |
// don't forget to return a promise here | |
return new Promise((resolve, reject) => { | |
// we mock a promise here. Just do fetch() for a real life example | |
setTimeout(() => { | |
resolve({ | |
a: 2 | |
}) | |
}, 1000) | |
}).then(data => { | |
this.setState({data}) | |
}) | |
} | |
// will do: | |
// - this.setState({isLoading: true, isLoaded: false, hasError: false}) promise created | |
// - this.setState({isLoading: false, isLoaded: true, hasError: false}) promise resolved | |
// - this.setState({isLoading: false, isLoaded: false, hasError: true}) promise rejected | |
@promiseable | |
componentDidMount() { | |
// anything can be in this function, but it has to return a promise | |
return this.loadData() | |
} | |
// will display a spinner while this.state.isLoaded !== true | |
// will display error message if this.state.hasError === true | |
@renderWhenLoaded | |
render() { | |
return <Calendar getElm={this.getElm.bind(this)} date={this.state.date}/> | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment