Skip to content

Instantly share code, notes, and snippets.

@Mehuge
Last active June 6, 2018 08:58
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 Mehuge/58753951a70f3c7ef08d330517f9c4b2 to your computer and use it in GitHub Desktop.
Save Mehuge/58753951a70f3c7ef08d330517f9c4b2 to your computer and use it in GitHub Desktop.
Fetch component
import * as React from 'react';
import { ISessionContext } from '../contexts/Session';
import Spinner from './Spinner';
import './Fetch.css';
let uriId = 0;
interface IFetchState {
state: string;
result?: any;
}
interface IFetchProps {
method?: string;
uri: string;
token?: string;
body?: any;
refresh?: number;
onLoad: (results: any, context?: ISessionContext) => void;
onFail?: (error: any, context?: ISessionContext) => void;
context?: ISessionContext;
}
export class Fetch extends React.PureComponent<IFetchProps, IFetchState> {
// private controller?: AbortController;
private id: number = 0;
private aborter: AbortController | null;
private timer: any;
constructor(props: IFetchProps) {
super(props);
this.state = { state: 'init' };
}
public componentDidMount() {
if (this.props.uri) {
this.run(this.props.uri);
}
}
public componentWillReceiveProps(next: IFetchProps) {
const props = this.props;
if (next.uri && next.uri !== props.uri) {
this.cancel();
this.run(next.uri);
}
}
public render() {
switch(this.state.state) {
case 'ready':
return null;
}
return (
<div className="fetch-spinner">
<Spinner name="swirls" zoom={5}/>
</div>
);
}
public componentWillUnmount() {
this.cancel();
}
private async run(uri: string) {
const id = this.id = uriId ++;
const method = this.props.method || (this.props.body ? 'POST' : 'GET');
console.log(`FETCH[${id}]: ${uri}`);
this.setState({ state: 'loading' });
const headers: any = {
'Accept': 'application/json',
'Content-Type': 'application/json'
};
if (this.props.token) headers.token = this.props.token;
let response;
const aborter = this.aborter = new AbortController();
try {
response = await fetch(uri, {
method,
headers,
body: JSON.stringify(this.props.body),
signal: aborter.signal,
});
} catch(e) {
console.error(e);
if (this.props.onFail) {
this.props.onFail({ status: 0, statusText: e.message });
}
return;
}
if (response.ok) {
const contentType = (response.headers.get('Content-Type') || '').split(';')[0];
if (contentType === 'application/json') {
const json = await response.json();
this.setState({ state: 'ready' });
if (json.status === 200) {
this.props.onLoad(json, this.props.context);
if (this.props.refresh) {
this.timer = setTimeout(() => this.run(uri), this.props.refresh * 1000);
}
} else {
switch(json.status) {
case 403:
if (this.props.context) {
this.props.context.setUser();
return;
}
break;
}
if (this.props.onFail) {
this.props.onFail(json, this.props.context);
}
}
}
} else {
console.error(response);
if (this.props.onFail) {
this.props.onFail({
status: response.status,
statusText: response.statusText,
});
}
}
}
private cancel() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
if (this.aborter) {
this.aborter.abort();
this.aborter = null;
}
}
}
export default Fetch;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment