Build a MERN stack todo app using react-query
Tags: react, webdev, javascript, tutorial
Published: 2020-10-19 16:03:01

---
title: Build a MERN stack todo app using react-query
published: true
tags: #react, #webdev, #javascript, #tutorial
---

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](https://github.com/ashiqdev/React-query-TodoList)