- Fetches data from an async function
- Allows you to manipulate the data à la Redux
- Will throw out outdated API calls by default
Basic usage:
import React, {FC} from 'react';
import useData from 'use-data';
import {someApi} from './someApi';
const FunctionalComponent: FC<{someProp: string}> = ({someProp}) => {
const {loading, error, data} = useData(() => someApi(someProp));
if (error) return <p>Error...</p>;
if (!data || loading) return <p>Loading...</p>;
return <p>{data.someString}</p>;
};
Expand for more advanced usage:
import React, {FC, useEffect} from 'react';
import useData from 'use-data';
import {getUser} from './getUserAPI';
const FunctionalComponent: FC<{userId: string}> = ({userId}) => {
const {loading, error, data, fireFetch, setData} = useData(
() => getUser(userId),
{username: '', age: 0},
{fireOnMount: false, takeEvery: true},
);
useEffect(() => {
// Wait for userId to fetch (see fireOnMount is false).
if (userId) fireFetch();
}, [fireFetch, userId]);
const handleSetUsername = () => {
// Uses the function option for newData in order to only change username.
setData(oldUser => ({...oldUser!, username: 'John Doe'}));
};
if (error) return <p>Error...</p>;
if (!data || loading) return <p>Loading...</p>;
return (
<>
<p>{data.username}</p>
<button onClick={handleSetUsername}>
{"Set username to 'John Doe'"}
</button>
</>
);
};
Expand for setting up hook with Context:
import React, {createContext, FC} from 'react';
import useData, {StatusObject} from 'use-data';
import {getName} from './getNameAPI';
interface ContextData {
firstName: string;
lastName: string;
}
export const NameContext = createContext<StatusObject<ContextData>>({
loading: false,
error: new Error('No Provider'),
data: null,
fireFetch: () => new Error('No Provider'),
setData: () => new Error('No Provider'),
});
export const NameProvider: FC = ({children}) => {
const statusObject = useData(getName);
return (
<NameContext.Provider value={statusObject}>{children}</NameContext.Provider>
);
};
The useData
hook is the only non-type export. The type definition is as
follows:
declare function useData<D>(
asyncFetch: () => Promise<D>,
initialData?: D,
options?: UseDataOptions,
): StatusObject<D>;
If you are using this in a TypeScript project, you do not need to provide a type
for the generic D
, as it will be automatically parsed from the return type of
asyncFetch
. However in some cases it may be desirable to define it (see more
information in the initialData
section).
It accepts three arguments:
Any async fetch function that you want to use to fetch data from. The type will be automatically parsed from the return type.
Example usage:
// If your function takes no arguments, you can pass it in directly:
const {loading, error, data} = useData(asyncFetch);
// If it requires arguments, wrap it in an arrow function:
const {loading, error, data} = useData(() => asyncFetch(someValue));
Initial data must match the return type of asyncFetch
, or the generic D
. For
example, the following will result in an error:
const {loading, error, data} = useData(
async () => {
return {a: {b: 'b contents'}};
},
{a: null},
);
This is because it expects initialData
to be of type { a: { b: string; }; }
.
To fix this, you will need to define the generic:
const {loading, error, data} = useData<{a: null | {b: string}}>(
async () => {
return {a: {b: 'b contents'}};
},
{a: null},
);
This will allow the property a
to be either null or { b: string; }
.
Options is an optional object that has the following structure:
export interface UseDataOptions {
fireOnMount?: boolean;
takeEvery?: boolean;
}
fireOnMount
- Default: true. Should the hook fire theasyncFetch
on mount.takeEvery
- Default: false. Should the hook take every call rather than throwing out active calls when new ones are made.
The hook returns an object with 5 properties:
loading
- True if currently fetching.error
- The error object if your fetch fails, or null if not failed.data
- The data from your async fetch, or null if not fetched.fireFetch
- Fire the async function that was provided to useData. You may pass it an async function to call instead ofasyncFetch
,setData
- Mutate the data. Either a function that takes old data and returns the new data, or data of typeD
. Calling this will turnerror
to null. Additionally takes a parameter to stop loading when called (loading will continue by default).
interface UseDataState<D> {
loading: boolean;
error: Error | null;
data: D | null;
}
interface StatusObject<D> extends UseDataState<D> {
fireFetch: (newAsyncFetch?: () => Promise<D>) => void;
setData: (
newData: D | ((oldData: D | null) => D),
stopLoading?: boolean,
) => void;
}