React-query is a Hooks for fetching, caching and updating asynchronous data in React. Many of us use redux or context API to manage our server state (asynchronous state). But What if we stop trying to manage our server state in our frontend code and instead treat it like a cache that just needs to be updated periodically? I think this will make our code more accessible to pure frontend developers.
Fetch Todos
const fetchPosts = async () => {
const {
data: { todos },
} = await axios.get(`http://localhost:7777/api`);
return todos;
};
const { data: todos, isLoading, error } = useQuery("tasks", fetchPosts);
You can see from the above snippet, I give 2 argument on useQuery
where tasks
is the key (we can refetch the data by using that key) and fetchPosts
is the callback function in which I call my express-API.
useQuery
gives data, isLoading, error
state. It reduces lots of boilerplate code.
Create a todo.
We will create todoItem using useMutation
Todo form
<form
className="form"
onSubmit={(e) => {
e.preventDefault();
mutate({ task: value });
reset();
}}
>
<input
type="text"
className="form__box"
onChange={onChangeHandler}
name="task"
value={value}
placeholder="Enter a task..."
required
/>
<button className="form__button">Add Task</button>
</form>;
useMutation
const [mutate] = useMutation(
(values) => axios.post("http://localhost:7777/api", values),
{
onSettled: () => {
queryCache.refetchQueries("tasks");
},
}
);
I have called the mutate
function when the form is submitted. and gave newTask
as an argument. useMutation
got that value as a parameter and send a post request to my express API. as a result a todo is created.
But, we can take this to next level. what if I want to see the submitted todo task before the api call is finished? Here comes an optimistic update
. We will optimistically update the UI for a better User experience.
Optimistic Update
const [mutate] = useMutation(
(values) => axios.post("http://localhost:7777/api", values),
{
onMutate: (newTask) => {
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
queryCache.cancelQueries("tasks");
// optimistic update
const previousTasks = queryCache.getQueryData("tasks");
queryCache.setQueryData("tasks", (old) => [...old, newTask]);
return () => queryCache.setQueryData("tasks", previousTasks);
},
onError: (error, newTask, rollback) => {
// If there is an errror, then we will reset the tasks to previous tasks
rollback();
},
onSettled: () => {
queryCache.refetchQueries("tasks");
},
}
);
Look we have two new methods. one is onMutate
another is onError
. onMutate
will call as soon as the form is submitted. so on this method, I am updating the todo list and show the updated list to the user.
// optimistic update
const previousTasks = queryCache.getQueryData("tasks");
queryCache.setQueryData("tasks", (old) => [...old, newTask]);
But, what if an error occurred? for example, user goes offline as soon as he submitted the new todo? here comes the onError
method. It will call the rollback
function (the function which is returned from onMutate
) and on cache, we will set the previous list as listItems array.
And after that, we will refetch the tasks
list on onSettled
method.
I think, If you understand create todo
, you will easily understand delete and update items.
Update Item Form
<form
onSubmit={(e) => {
e.preventDefault();
mutate({ task: value });
reset();
toggle(false);
}}
>
<input
type="text"
autoFocus
className="edit"
onChange={handleChange}
value={value}
/>
</form>;
Update Item Mutation
const [mutate] = useMutation(
(newTodo) => axios.put(`http://localhost:7777/api/${todo._id}`, newTodo),
{
onMutate: (newTask) => {
console.log(newTask);
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
queryCache.cancelQueries("tasks");
// Snapshot the previous value
const previousTask = queryCache.getQueryData("tasks");
// Optimistically update to the new value
queryCache.setQueryData("tasks", (oldTasks) =>
oldTasks.map((item) =>
item._id === todo._id ? { ...item, task: newTask.task } : item
)
);
return () => queryCache.setQueryData("tasks", previousTask);
},
onError: (error, newTask, rollback) => {
// If there is an errror, then we will reset the tasks to previous tasks
rollback();
},
onSettled: (data, error, newTask) => {
queryCache.refetchQueries("tasks");
},
}
);
Delete Item
const [mutateToggle] = useMutation(
(values) => axios.patch(`http://localhost:7777/api/${todo._id}`),
{
onSettled: () => {
queryCache.refetchQueries('tasks');
},
}
);
You will get the full working code on this repository.
React-query Todo App
Top comments (0)