API Hooks
This guide is an introduction to the various hooks available for accessing the api in the front end.
Hooks and API requests
Hooks are a core piece of React provide a useful set of primitives for reusing logic across components.
API requests in Rhino use axios for networking and react-query to provide a robust asynchronous cache.
Making API calls
Rhino provides five common hooks to match the five common CRUD actions, two for reading/querying data:
- useModelShow
- useModelIndex
and three for altering/mutating data:
- useModelCreate
- useModelUpdate
- useModelDestroy
API Query Hooks
useModelShow and useModelIndex are built on top of useQuery and return everything that this hook does.
useModelShow
useModelShow takes 3 arguments:
const { resource } = useModelShow(model, id, options);
modela string for the model name or an already existing model object (from useModel or getModel)ida value for the id of the object to fetch (omitted or null for singular models)optionsnetworkOptionsa set of options passed to axiosqueryOptionsa set of options passed to useQuery (for instance to set theenabledoption)
and in addition to the useQuery returns, useModelShow returns:
modela memoized model used to construct the queryresourcea memoized copy of the model (or null if not yet available)
useModelIndex
useModelIndex takes 2 arguments:
const { resources } = useModelIndex(model, options);
modela string for the model name or an already existing model object (from useModel or getModel)optionssearcha string for full text search (of fields configured server side)filteran object representing a set of filters See Rest API Filteringordera string resprenting the sort orderlimita number representing the maximum number of resultsoffseta number representing the start of the resultsnetworkOptionsa set of options passed to axiosqueryOptionsa set of options passed to useQuery (for instance to set theenabledoption)
and in addition to the useQuery returns, useModelIndex returns:
modela memoized model used to construct the queryresourcesa memoized copy the model list object from the api with results and total (resources.results, resources.total) (or null if not yet available)resultsa memoized copy of results (or null if not yet available)totala memoized copy of total - the total number of matching objects regardless of pagination (or null if not yet available)
Recipes
Passing query modifiers
const { resources } = useModelIndex(model, {
search: 'Test'
filter: {
published: true,
},
order: 'name',
limit: 10,
offset: 10
});
Disabling a Query
const { resources } = useModelIndex(model, {
queryOptions: { enabled: !!user },
});
See also: Dependent Queries
Refreshing More Frequently
const { resources } = useModelIndex(model, {
queryOptions: { refetchInterval: 15 * 1000 },
});
Checking status
It may be tempting to do something like:
const { resource } = useModelShow('blog', 23);
if (!resource) return <Spinner>;
but it is better to rely on the status checks in react-query:
const { isSuccess, resource } = useModelShow('blog', 23);
if (!isSuccess) return <Spinner>;
API Mutation Hooks
useModelCreate, useModelUpdate and useModelDestroy are built on top of useMutation and return everything that this hook does.
The API also returns the object in its form post-mutation so you can react to the new state.
useModelCreate
useModelCreate takes 2 arguments:
const { mutation } = useModelCreate(model, mutationOptions);
modela string for the model name or an already existing model object (from useModel or getModel)mutationOptionsa set of options passed to useMutation (for instance to set theenabledoption)
and in addition to the [useMutation returns, useModelCreate returns:
modela memoized model used to construct the queryresourcea memoized copy of the model (or null if not yet available)
Recipes
Acting On Success
const { mutate } = useModelCreate("blog_post");
const handleCreate = () => mutate({name "test"}, {onSuccess: (data) => console.log("Created id", data?.data?.id)})
useModelUpdate
useModelUpdate takes 2 arguments:
const { mutation } = useModelUpdate(model, mutationOptions);
modela string for the model name or an already existing model object (from useModel or getModel)mutationOptionsa set of options passed to useMutation (for instance to set theenabledoption)
and in addition to the useMutation returns, useModelUpdate returns:
modela memoized model used to construct the queryresourcea memoized copy of the model (or null if not yet available)
When calling the mutation:
const { mutate } = useModelUpdate("blog_post");
const handleSave = () => mutate({id: 4, name "test"})
the object passed to mutate must contain and id field.
Recipes
Acting On Success
const { mutate } = useModelUpdate("blog_post");
const handleSave = () => mutate({id: 4, name "test"}, {onSuccess: (data) => console.log("Updated name to", data?.data?.name)})
useModelDestroy
useModelDestroy takes 2 arguments:
const { mutation } = useModelDestroy(model, mutationOptions);
modela string for the model name or an already existing model object (from useModel or getModel)mutationOptionsa set of options passed to useMutation (for instance to set theenabledoption)
and in addition to the useMutation returns, useModelDestroy returns:
modela memoized model used to construct the queryresourcea memoized copy of the model (or null if not yet available)
When calling the mutation:
const { mutate } = useModelDestroy("blog_post");
const handleDelete = () => mutate({ id: 4 });
the object passed to mutate must contain and id field.
Recipes
Acting On Success
const { mutate } = useModelDestroy("blog_post");
const handleDelete = () =>
mutate(
{ id: 4 },
{
onSuccess: (data) =>
console.log("Destroyed object with name", data?.data?.name),
}
);
Building Custom Hooks
The built in CRUD hooks themselves are composed of several lower level hooks, all of which can be re-used to build your own more customized hooks, for instance:
export const usePublishedBlogPosts = () => {
return useModelIndex("blog_post", {
filter: {
published: true,
},
});
};