Skip to content

Instantly share code, notes, and snippets.

@drwpow
Last active January 16, 2019 01:14
Show Gist options
  • Save drwpow/9799b69006e215ec925a8841d6e1487c to your computer and use it in GitHub Desktop.
Save drwpow/9799b69006e215ec925a8841d6e1487c to your computer and use it in GitHub Desktop.
React fetching data from REST endpoint with 16.7 React hooks

Simple, native React solution for fetching & caching data using hooks. Based on react-hooks-example.

Features

  • < 1KB (not including axios, but that can be easily swapped with fetch or any promise library)
  • Caches requests: will only re-fire if a request parameter has changed
  • Component can be re-rendered; request won’t fire unless necessary

Setup

npm install react@next react-dom@next axios

This also requires jest-mock-axios for Jest testing. Follow the instructions on that repo for setup instructions (not covered here).

import * as React from 'react';
import { mount } from 'enzyme';
import mockAxios from 'jest-mock-axios';
import Query from '../query';
const children = jest.fn();
const render = () =>
mount(
<Query options={{ url: 'https://mock.dev' }}>
{res => {
children(res);
return <div>{JSON.stringify(res)}</div>; // We just need to render _something_ other than null
}}
</Query>
);
describe('Query component', () => {
beforeEach(() => children.mockReset());
afterEach(() => mockAxios.reset());
it('returns loading when mounted', () => {
render();
expect(children.mock.calls[0][0]).toEqual({ loading: true });
});
it('returns data when successful', () => {
const data = { foo: 'bar' };
render();
mockAxios.mockResponse({ data });
expect(children).toHaveBeenCalledWith({ data, loading: false });
});
it('returns error when not successful', () => {
const error = { status: 500 };
render();
mockAxios.mockError(error);
expect(children).toHaveBeenCalledWith({ error, loading: false });
});
});
This file has been truncated, but you can view the full file.
import * as React from 'react';
import axios, { AxiosRequestConfig } from 'axios';
export interface QueryChildrenProps<Data> {
data?: Data;
error?: any;
loading: boolean;
slow: boolean;
}
interface QueryProps<Data> {
children(props: QueryChildrenProps<Data>): JSX.Element;
options: AxiosRequestConfig;
wait?: number;
}
function Query<Data>(props: QueryProps<Data>) {
const { children, options, wait = 300 } = props;
const [data, setData] = React.useState(undefined);
const [error, setError] = React.useState(undefined);
const [loading, setLoading] = React.useState(true);
const [slow, setSlow] = React.useState(false);
const req = () => {
// reset loading & error but keep data
setLoading(true);
setError(undefined);
axios(options)
.then(response => {
setData(response.data);
setLoading(false);
})
.catch(res => {
// On error, trigger Rollbar snippet
window.onerror(res, window.location.href, undefined, undefined, res);
setError(r
@drwpow
Copy link
Author

drwpow commented Nov 30, 2018

Example usage:

import * as React from 'react';
import Query from 'lib/query';

// Note: since we’re using hooks, we have to use a function component (at least for now)
const FetchExample: React.SFC = () => {
  const [getCount, setGetCount] = React.useState(0);
  const [postCount, setPostCount] = React.useState(0);

  return (
    <div>
      GET count: {getCount}
      <br />
      <button onClick={() => setGetCount(getCount + 1)}>GET</button>
      <br />
      {getCount > 0 && (
        <code>
          <Query options={{ url: `https://swapi.co/api/people/${getCount}/` }}>
            {({ data, loading, error }) => (
              <div>
                Data: {JSON.stringify(data)}
                <br />
                Loading: {JSON.stringify(loading)}
                <br />
                Error: {JSON.stringify(error)}
                <br />
              </div>
            )}
          </Query>
        </code>
      )}
      <hr />
      POST count: {postCount}
      <br />
      <button onClick={() => setPostCount(postCount + 1)}>POST</button>
      <br />
      {postCount > 0 && (
        <code>
          <Query
            options={{
              url: 'https://swapi.co/api/people/',
              method: 'POST',
            }}
          >
            {({ data, loading, error }) => (
              <div>
                Data: {JSON.stringify(data)}
                <br />
                Loading: {JSON.stringify(loading)}
                <br />
                Error: {JSON.stringify(error)}
                <br />
              </div>
            )}
          </Query>
        </code>
      )}
    </div>
  );
};

export default FetchExample;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment