# useMutation()

## useMutation() là gì?

* `useMutation()` là một hook phục vụ cho việc gửi yêu cầu **POST**, **PUT**, **DELETE** dữ liệu lên trên server

## Cú pháp của useMutation()

```tsx
const myMutation = useMutation({
    /* Cấu hình cho useMutation() */
})
```

## Các thuộc tính, phương thức trả về từ useMutation()

<table><thead><tr><th width="185">Property</th><th>Return value</th></tr></thead><tbody><tr><td><code>status</code></td><td><p><code>loading</code>: nếu mutation đang thực thi</p><p><code>success</code>: nếu mutation thực thi thành công</p><p><code>error</code>: nếu mutation thực thi thất bại</p></td></tr><tr><td><code>data</code></td><td>Trả về dữ liệu nhân được về từ server sau khi mutation thực thi thành công</td></tr><tr><td><code>error</code></td><td>Trả về lỗi mà server phản hồi khi mutation thực thi thất bại</td></tr></tbody></table>

<table><thead><tr><th width="183">Method</th><th>Explaination</th></tr></thead><tbody><tr><td><code>mutate()</code></td><td><p>Dùng để trigger request (<strong>POST</strong>, <strong>PUT</strong>, <strong>DELETE</strong>): Cú pháp: <code>mutate(variables, { onSuccess: (data) => {}, onError: (error) => {} });</code></p><ul><li><code>variables</code>: parameter để truyền vào mutation function (đây có thể là dữ liệu phần body của request). Nếu có nhiều parameter muốn truyền vào mutation function, phải gói lại trong một object vì đây là biến đơn</li><li><code>data</code>: dữ liệu nhận được từ server sau khi mutation thực thi thành công, nó chính là thuộc tính <code>data</code> trả về từ <code>useMutation()</code></li><li><code>error</code>: lỗi mà server trả về sau khi mutation thực thi thất bại, nó chính là thuộc tính <code>error</code> trả về từ <code>useMutation()</code></li></ul></td></tr><tr><td><code>mutateAsync()</code></td><td>Cấu hình giống như <code>mutate()</code>. Chỉ khác <code>mutate()</code> trả về kiểu void nên không thể dùng <code>await</code>, còn <code>mutateAsync()</code> trả về một <strong>Promise</strong> nên có thể sử dụng kèm với từ khóa <code>await</code> để chờ đợi mutation thực thi xong</td></tr></tbody></table>

## Cấu hình cho useMutation()

<table><thead><tr><th width="190.33333333333331">Config</th><th width="123">Type</th><th>Explaination</th></tr></thead><tbody><tr><td><code>mutationFn</code></td><td>Function</td><td>Nhận vào một function <code>return Promise</code> để thực hiện việc gửi request lên server</td></tr><tr><td><code>onSuccess</code></td><td>Function</td><td><p>Nhận vào một function có parameter là <code>data</code>: <code>onSuccess: (data) => {}</code></p><ul><li>Parameter <code>data</code> cũng chính là thuộc tính <code>data</code> được trả về từ <code>useMutation()</code></li><li>Hàm được gọi ngay sau khi mutation thực thi thành công.</li><li>Hàm này trả về <code>Promise</code> nên có thể đi kèm với <code>await</code></li></ul></td></tr><tr><td><code>onError</code></td><td>Function</td><td><p>Là một function nhận <code>error</code> (có thể là <code>AxiosError</code>) làm parameter:</p><p><code>onError: (error) => {}</code></p><ul><li>Parameter <code>error</code> cũng chính là thuộc tính <code>error</code> được trả về từ <code>useMutation()</code>.</li><li>Hàm sẽ chạy ngay sau khi có lỗi phát sinh trong khi mutation thực thi.</li><li>Hàm này trả về <code>Promise</code> nên có thể đi kèm với <code>await</code></li></ul></td></tr><tr><td><code>retry</code></td><td>Number</td><td>Mutation sẽ thực thi lại tối đa số lần ta chỉ định nếu server phản hồi lỗi. Sau số lần retry này nếu server vẫn phản hồi lại lỗi thì lúc đó mới thực sự gửi lỗi về client</td></tr></tbody></table>

## Ví dụ về useMutation():

```tsx
import axios, { AxiosError } from "axios";
import { useMutation } from "@tanstack/react-query";
import { useRef } from "react";

const http = axios.create({
  baseURL: "https://6404cc9280d9c5c7bad0e2f2.mockapi.io/api/v1/"
});

interface Task {
  name: string;
}

const httpPost = async (body: Task) => {
  const { data } = await http.post<Task>("tasks", body);
  return data;
};

export default function App() {
  const inputRef = useRef<HTMLInputElement>(null);
  const postTaskMutation = useMutation({
    mutationFn: httpPost,
    onSuccess: (data) => {
      console.log(data);
    },
    onError: (error: AxiosError) => {
      console.log(error);
    }
  });
  return (
    <>
      <input ref={inputRef} />
      <button
        onClick={() =>
          postTaskMutation.mutate({ name: inputRef.current?.value as string })
        }
      >
        Add new task
      </button>
    </>
  );
}
```

## 🆘🆘🆘Lưu ý:

* Nếu ta truyền trực tiếp đối tượng function vào `mutationFn` thì function đó chỉ nhận **MỘT** parameter duy nhất và đối số được truyền cho parameter duy nhất đó chính là giá trị của `variables` ở phương thức `mutate()`. Ví dụ:

```tsx
interface Task {
    name: string
    deadline: Date
}

const http = axios.create({
    baseURL: "https://6404cc9280d9c5c7bad0e2f2.mockapi.io/api/v1/"
});

const httpPost = async (body: Task) => {
    const { data } = await http.post<Task>('tasks', body);
    return data;
}

const postTaskMutation = useMutation({
    mutationFn: httpPost, //Truyền trực tiếp đối tượng function, thì function này chỉ có MỘT parameter duy nhất
});

const handlePostTask = () => {
    updateTaskMutation.mutate({name: 'X', deadline: new Date()}, {
        onSuccess: (data) => { },
        onError: (error: AxiosError) => {
            console.log(error);
        },
    });
};
```

* Nếu muốn đối tượng function của `mutationFn` có từ hai parameter trở lên (như phương thức **PUT** cần `id` và `body`), thì ta phải truyền function đó dưới dạng callback. Lúc này cho `variables` ở phương thức `mutate()` bằng `undefined`. Ví dụ:

```tsx
const http = axios.create({
    baseURL: "https://6404cc9280d9c5c7bad0e2f2.mockapi.io/api/v1/"
});

const httpPut = async (id: number, body: Task) => {
    const { data } = await http.put<Task>(`tasks/${id}`, body);
    return data;
}

const updateTaskMutation = useMutation({
    mutationFn: () => httpPut(reqId, reqData), // Truyền mutation function dưới dạng callback
});
const handleUpdateTask = () => {
    //Chỉ định giá trị của variables thành "undefined" do không dùng đến
    updateTaskMutation.mutate(undefined, {
        onSuccess: (data) => { },
        onError: (error: AxiosError) => {
            console.log(error);
        },
    });
};
```
