Comment on page
useFetch
It follows the
stale-while-revalidate
cache invalidation strategy popularized by HTTP RFC 5861. useFetch
first returns the data from cache (stale), then sends the request (revalidate), and finally comes with the up-to-date data again.The last value will be kept between command runs.
function useFetch<T, U>(
url: string,
options?: RequestInit & {
parseResponse?: (response: Response) => Promise<T>;
initialData?: U;
keepPreviousData?: boolean;
execute?: boolean;
onError?: (error: Error) => void;
onData?: (data: T) => void;
onWillExecute?: (args: [string, RequestInit]) -> void;
}
): AsyncState<T> & {
revalidate: () => void;
mutate: MutatePromise<T | U | undefined>;
};
url
is the string representation of the URL to fetch.
With a few options:
options.parseResponse
is a function that accepts the Response as an argument and returns the data the hooks will return. By default, the hook will returnresponse.json()
if the response has a JSONContent-Type
header orresponse.text()
otherwise.
options.keepPreviousData
is a boolean to tell the hook to keep the previous results instead of returning the initial value if there aren't any in the cache for the new arguments. This is particularly useful when used for data for a List to avoid flickering. See Argument dependent on List search text for more information.
options.initialData
is the initial value of the state if there aren't any in the Cache yet.
options.execute
is a boolean to indicate whether to actually execute the function or not. This is useful for cases where one of the function's arguments depends on something that might not be available right away (for example, depends on some user inputs). Because React requires every hook to be defined on the render, this flag enables you to define the hook right away but wait until you have all the arguments ready to execute the function.options.onError
is a function called when an execution fails. By default, it will log the error and show a generic failure toast with an action to retry.options.onData
is a function called when an execution succeeds.options.onWillExecute
is a function called when an execution will start.
Returns an object with the AsyncState corresponding to the execution of the fetch as well as a couple of methods to manipulate it.
revalidate
is a method to manually call the function with the same arguments again.mutate
is a method to wrap an asynchronous update and gives some control over how theuseFetch
's data should be updated while the update is going through. By default, the data will be revalidated (eg. the function will be called again) after the update is done. See Mutation and Optimistic Updates for more information.
import { Detail, ActionPanel, Action } from "@raycast/api";
import { useFetch } from "@raycast/utils";
export default function Command() {
const { isLoading, data, revalidate } = useFetch("https://api.example");
return (
<Detail
isLoading={isLoading}
markdown={data}
actions={
<ActionPanel>
<Action title="Reload" onAction={() => revalidate()} />
</ActionPanel>
}
/>
);
}
By default, when an argument passed to the hook changes, the function will be executed again and the cache's value for those arguments will be returned immediately. This means that in the case of new arguments that haven't been used yet, the initial data will be returned.
This behaviour can cause some flickering (initial data -> fetched data -> arguments change -> initial data -> fetched data, etc.). To avoid that, we can set
keepPreviousData
to true
and the hook will keep the latest fetched data if the cache is empty for the new arguments (initial data -> fetched data -> arguments change -> fetched data).import { useState } from "react";
import { List, ActionPanel, Action } from "@raycast/api";
import { useFetch } from "@raycast/utils";
export default function Command() {
const [searchText, setSearchText] = useState("");
const { isLoading, data } = useFetch(`https://api.example?q=${searchText}`, {
// to make sure the screen isn't flickering when the searchText changes
keepPreviousData: true,
});
return (
<List isLoading={isLoading} searchText={searchText} onSearchTextChange={setSearchText} throttle>
{(data || []).map((item) => (
<List.Item key={item.id} title={item.title} />
))}
</List>
);
}
In an optimistic update, the UI behaves as though a change was successfully completed before receiving confirmation from the server that it was - it is being optimistic that it will eventually get the confirmation rather than an error. This allows for a more responsive user experience.
You can specify an
optimisticUpdate
function to mutate the data in order to reflect the change introduced by the asynchronous update.When doing so, you can specify a
rollbackOnError
function to mutate back the data if the asynchronous update fails. If not specified, the data will be automatically rolled back to its previous value (before the optimistic update).import { Detail, ActionPanel, Action, showToast, Toast } from "@raycast/api";
import { useFetch } from "@raycast/utils";
export default function Command() {
const { isLoading, data, mutate } = useFetch("https://api.example");
const appendFoo = async () => {
const toast = await showToast({ style: Toast.Style.Animated, title: "Appending Foo" });
try {
await mutate(
// we are calling an API to do something
fetch("https://api.example/append-foo"),
{
// but we are going to do it on our local data right away,
// without waiting for the call to return
optimisticUpdate(data) {
return data + "foo";
},
}
);
// yay, the API call worked!
toast.style = Toast.Style.Success;
toast.title = "Foo appended";
} catch (err) {
// oh, the API call didn't work :(
// the data will automatically be rolled back to its previous value
toast.style = Toast.Style.Failure;
toast.title = "Could not append Foo";
toast.message = err.message;
}
};
return (
<Detail
isLoading={isLoading}
markdown={data}
actions={
<ActionPanel>
<Action title="Append Foo" onAction={() => appendFoo()} />
</ActionPanel>
}
/>
);
}
An object corresponding to the execution state of the function.
// Initial State
{
isLoading: true, // or `false` if `options.execute` is `false`
data: undefined,
error: undefined
}
// Success State
{
isLoading: false,
data: T,
error: undefined
}
// Error State
{
isLoading: false,
data: undefined,
error: Error
}
// Reloading State
{
isLoading: true,
data: T | undefined,
error: Error | undefined
}
A method to wrap an asynchronous update and gives some control about how the
useFetch
's data should be updated while the update is going through.export type MutatePromise<T> = (
asyncUpdate?: Promise<any>,
options?: {
optimisticUpdate?: (data: T) => T;
rollbackOnError?: boolean | ((data: T) => T);
shouldRevalidateAfter?: boolean;
}
) => Promise<any>;
Last modified 29d ago