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);
model
a string for the model name or an already existing model object (from useModel or getModel)id
a value for the id of the object to fetch (omitted or null for singular models)options
networkOptions
a set of options passed to axiosqueryOptions
a set of options passed to useQuery (for instance to set theenabled
option)
and in addition to the useQuery returns, useModelShow returns:
model
a memoized model used to construct the queryresource
a memoized copy of the model (or null if not yet available)
useModelIndex
useModelIndex takes 2 arguments:
const { resources } = useModelIndex(model, options);
model
a string for the model name or an already existing model object (from useModel or getModel)options
search
a string for full text search (of fields configured server side)filter
an object representing a set of filters See Rest API Filteringorder
a string resprenting the sort orderlimit
a number representing the maximum number of resultsoffset
a number representing the start of the resultsnetworkOptions
a set of options passed to axiosqueryOptions
a set of options passed to useQuery (for instance to set theenabled
option)
and in addition to the useQuery returns, useModelIndex returns:
model
a memoized model used to construct the queryresources
a memoized copy the model list object from the api with results and total (resources.results, resources.total) (or null if not yet available)results
a memoized copy of results (or null if not yet available)total
a 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);
model
a string for the model name or an already existing model object (from useModel or getModel)mutationOptions
a set of options passed to useMutation (for instance to set theenabled
option)
and in addition to the [useMutation returns, useModelCreate returns:
model
a memoized model used to construct the queryresource
a 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);
model
a string for the model name or an already existing model object (from useModel or getModel)mutationOptions
a set of options passed to useMutation (for instance to set theenabled
option)
and in addition to the useMutation returns, useModelUpdate returns:
model
a memoized model used to construct the queryresource
a 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);
model
a string for the model name or an already existing model object (from useModel or getModel)mutationOptions
a set of options passed to useMutation (for instance to set theenabled
option)
and in addition to the useMutation returns, useModelDestroy returns:
model
a memoized model used to construct the queryresource
a 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,
},
});
};