Skip to content

Instantly share code, notes, and snippets.

@schicks
Last active December 6, 2018 21:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save schicks/fdfbb091a1e7884e6a0f2334dcf5bad9 to your computer and use it in GitHub Desktop.
Save schicks/fdfbb091a1e7884e6a0f2334dcf5bad9 to your computer and use it in GitHub Desktop.
Declarative data from rest apis in react
//@flow
import * as React from 'react'
import axios from 'axios'
type JsonType = number | string | { [string]: JsonType } | JsonType[] | null
type Props = {
uri: string,
children: React.ComponentType<{ loading: boolean, error: boolean, data?: JsonType }>,
transform: JsonType => JsonType,
cacheLimit: number
}
type State = {
isLoading: boolean,
hasErrored: boolean,
cache: { [string]: JsonType },
promiseCache: { [string]: Promise<JsonType> },
tenants: string[]
}
export default class Rest extends React.Component<Props, State> {
static defaultProps = {
transform: (a: JsonType): JsonType => a,
cacheLimit: 10
}
constructor(props: Props) {
super(props)
const promised = axios.get(props.uri)
this.state = {
hasErrored: false,
isLoading: true,
cache: {},
promiseCache: { [props.uri]: promised },
tenants: []
}
promised.then(this.cache).catch(this.error)
}
componentDidUpdate({ uri: previousUri }: Props) {
if (previousUri !== this.props.uri) {
this.setState({ isLoading: true })
if (this.state.promiseCache[this.props.uri]) {
const promised = this.state.promiseCache[this.props.uri]
promised.then(this.cache).catch(this.error)
} else {
this.setState(state => {
const promised = axios.get(this.props.uri)
promised.then(this.cache).catch(this.error)
const newState = {
cache: { ...state.cache },
promiseCache: {
...state.promiseCache,
[this.props.uri]: promised
},
tenants: state.tenants.includes(this.props.uri)
? state.tenants
: [this.props.uri, ...state.tenants]
}
if (newState.tenants.length > this.props.cacheLimit) {
const evicted = state.tenants.pop()
delete newState.promiseCache[evicted]
delete newState.cache[evicted]
}
return newState
})
}
}
}
cache = (resolution: JsonType) => {
this.setState(state => {
if (state.promiseCache[this.props.uri]) {
const newState = {
isLoading: false,
cache: {
...state.cache,
[this.props.uri]: this.props.transform(resolution)
}
}
return newState
} else return { isLoading: false }
})
}
error = () => {
this.setState({ isLoading: false, hasErrored: true })
}
render() {
const Renderer = this.props.children
return (
<Renderer
error={this.state.hasErrored}
loading={this.state.isLoading}
data={this.state.cache[this.props.uri]}
/>
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment