What's a fetch?
A small disclaimer: Naming collisions are an unfortunate thing we have to deal with in software engineering and ReSift's "fetches" are no different. For now, please forget what you know about fetches or
window.fetch
because they are different from the fetches we define here.
A fetch is noun
ReSift has this concept of a fetch (used as a noun). Fetches are tokens you pass to different ReSift functions to get stuff about your fetch.
Here's the general workflow steps:
- create a fetch factory that will produce fetch instance/tokens.
- (within a component,) call the fetch factory to get a fetch instance (aka just a "fetch").
- pass that fetch into ReSift functions to get stuff
// call defineFetch to create a "fetch factory"
const makeGetPerson = defineFetch(/* ... */);
// ๐ this is the fetch factory
function MyComponent() {
// call the fetch factory with some ID to get a "fetch"
const getPerson = makeGetPerson('person-id-123');
// ๐ this is a fetch
// pass this fetch into ReSift functions to
// get stuff about your fetch
const status = useStatus(getPerson); // ๐
const data = useData(getPerson); // ๐
return (
// ๐
<Guard fetch={getPerson}>{(person) => <span>{person.name}</span>}</Guard>
);
}
We'll go over what you can do with a fetch in the next sections. For now, the takeaway is that fetches are tokens/nouns you can give to ReSift in exchange for a status or data.
Defining a fetch
In ReSift, you start by defining a fetch factory.
A fetch factory is a function that will produce fetch instances. It describes how you'll get data and how your data is related to other things in the cache (described in an upcoming doc).
To define a fetch, use the function defineFetch
from ReSift. Take a look at this next code example to get a sneak peak in to how to define a fetch.
Don't get too caught up in the example just yet. In the next doc, we'll go in depth into how to define a fetch.
makeGetPerson.js
import { defineFetch } from 'resift';
const makeGetPerson = defineFetch({
displayName: 'Get Person',
make: (personId) => ({
request: () => ({ http }) =>
http({
method: 'GET',
route: `/people/${personId}`,
}),
}),
});
export default makeGetPerson;
defineFetch
returns a fetch factory (e.g. makeGetPerson
).
A fetch factory is a producer of a type of fetch. When you call a fetch factory, you get a fetch instance.
// ๐ invoke this fetch factory...
const getPerson = makeGetPerson('person123');
// ๐
// ...to get a fetch instance
Making a fetch and pulling data from it
In order to make a fetch instance, you need to call the fetch factory with the arguments you define in make
of defineFetch
.
These arguments must be strings or numbers. ReSift will use these values to decide where to save data in its internal cache.
After you make the fetch instance, you can then use it to pull the data and the status of the request into your component by using the Guard
component and the useStatus
hook respectively.
Person.js
import React from 'react';
import { Guard, useStatus, isNormal, isLoading } from 'resift';
import makeGetPerson from './makeGetPerson';
import Spinner from './Spinner';
function Person({ personId }) {
// call the `makePersonFetch` "fetch factory" to get a "fetch" back
const getPerson = makeGetPerson(personId);
// these are the args from `make` ๐ above
// get the status
const status = useStatus(getPerson);
return (
<div>
{/* use the `isLoading` helper to show spinners */}
{isLoading(status) && <Spinner />}
{/* pass the fetch to the Guard component to get data */}
<Guard fetch={getPerson}>
{(person) => (
// ๐ person will never be null because of the Guard
<div>Hello, {person.name}!</div>
)}
</Guard>
</div>
);
}
export default Person;
The <Guard />
component takes a function as a child. This function is called by the <Guard />
component when the data is present and it's safe to render the contents of the Guard.
We recommend using Guard
s in your components. However, alternatively, you can use the useData
hook to pull the current value of the fetch. This value may be null
if there is no data available with the associated fetch.
import React from 'react';
import { useStatus, useData, isNormal, isLoading } from 'resift';
import makeGetPerson from './makeGetPerson';
import Spinner from './Spinner';
function Person({ personId }) {
const getPerson = makeGetPerson(personId);
const status = useStatus(getPerson);
// โ ๏ธ `person` may `null` if there is no data ready
const person = useData(getPerson);
return (
<div>
{/* use the `isLoading` helper to show spinners */}
{isLoading(status) && <Spinner />}
{person && <div>Hello, {person.name}!</div>}
</div>
);
}
export default Person;
Dispatching requests
The last example showed you how to pull data from a fetch instance but not how to initially dispatch the request.
Below is a modified example that will dispatch a request when the personId
changes (including the first mount).
import React, { useEffect } from 'react';
import { Guard, useDispatch, useStatus, isNormal, isLoading } from 'resift';
import makeGetPerson from './makeGetPerson';
import Spinner from './Spinner';
function Person({ personId }) {
// `useDispatch` to get back the `dispatch` function
const dispatch = useDispatch();
const getPerson = makeGetPerson(personId);
const status = useStatus(getPerson);
// Use an effect to watch for fetches in `personId` and
// re-fetch accordingly. This also can occur after the initial mount.
useEffect(() => {
dispatch(getPerson());
}, [dispatch, getPerson()]);
return (
<div>
{isLoading(status) && <Spinner />}
<Guard fetch={getPerson}>{(person) => <div>Hello, {person.name}!</div>}</Guard>
</div>
);
}
export default Person;
In the example above, this component is fetching and re-fetching the person based on the personId
.
This occurs because of the effect (via useEffect
) that watches the dependencies array (the second argument of useEffect
) and calls the callback when any value in the dependencies array changes. See the official React docs for usage of the Effect Hook for more info.
In this case, the value getPerson
will change when the personId
changes. If personId
changes, then getPerson
will change, and that will tell the effect to re-run and send off another request.
Clearing fetches from the cache
If you no longer have use for the data in the cache, you can optionally clear it using useClearFetch
.
NOTE: we don't expect you to be needing to clear the cache often. ReSift's cache is in-memory and will be reset on refresh or reboot.
import React from 'react';
import { Guard, useDispatch, useClearFetch } from 'resift';
import getResource from './getResource';
function Component() {
const clear = useClearFetch();
const dispatch = useDispatch();
useEffect(() => {
dispatch(getResource);
// clear fetch works great with `useEffect`'s clean-up phase
// https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup
return () => {
clear(getResource);
};
}, []);
return <Guard fetch={getResource}>{(resource) => <>{/* ... */}</>}</Guard>;
}
export default Component;
Fetches are global
An important thing to note is that ReSift fetches are global meaning that if a fetch has been completed and used in one component, it will be ready for any another component.
This is a key concept of ReSift fetches because global fetches also means global cache/state. Global state allows the lifecycles of a fetch to be split up across many different components enabling "fetch-then-render" and "render-as-you-fetch" patterns.
For example, one component can be responsible for reacting to some event to pre-fetch (i.e. dispatch a request for) data for another component. These components can live anywhere in the component tree because the state of the fetches are hoisted above the component tree (i.e. they're global).
Summary: The lifecycle of a fetch
defineFetch
returns a fetch factory (e.g.makeGetPerson
)- When
makeGetPerson
is called with apersonId
, it returns a fetch instance associated with that ID (e.g.getPerson
) - Then either:
- The fetch can be used to get the data and the status via
useStatus
and<Guard />
.
(e.g.const status = useStatus(getPerson)
) - The fetch instance can be invoked and dispatched to initiate the request.
(e.g.dispatch(getPerson())
)
- The fetch can be used to get the data and the status via
- Optionally, you can clear the data from the cache using
useClearFetch