React Query 사용 예제(3)

김재한·2023년 7월 2일
0

React 기초

목록 보기
9/9

1. useMutate

주로 서버의 데이터를 요청하는 GET 메서드의 경우 useQuery, useQueries를 사용한다.
하지만, 서버의 데이터를 바꾸는 POST, PUT, DELETE 메서드에 대해서는 useMutation을 사용하는 것을 권장한다.
이 때, onSuccess, onError와 같은 옵션을 사용해 데이터 변경후 처리를 커스텀하게 개발할 수 있다.

[ 데이터 변경 후 UI 적용 방법 ]

  • Invalidate Queries
  • 업데이트 된 데이터만 추가 / 수정 / 삭제
  • Optimistic Update (낙관적 업데이트)

1) Invalidate Queries

특정 쿼리키를 무효화시켜 데이터를 리패칭시키는 방법이다.

[ 예시 ]

//todos.js
const getTodos = async () => {
  const { data } = await axios.get("http://localhost:5000/todos");
  return data;
};

const addTodo = async (todo) => {
  const { data } = await axios.post("http://localhost:5000/todos", {
    todo,
    done: false,
  });

  return data;
};

const TodosPage = () => {
  const [todo, setTodo] = useState("");
  const queryClient = useQueryClient();

  const {
    data: todos,
    isLoading,
    isError,
    error,
  } = useQuery("todos", getTodos, {refetchOnWindowFocus: false});


  const { mutate } = useMutation(addTodo, {
    //성공시 쿼리 무효화
    onSuccess: () => {
      queryClient.invalidateQueries("todos");
    },
  });

  const onSubmit = useCallback(
    (e) => {
      e.preventDefault();
      mutate(todo);
      setTodo("");
    },
    [mutate, todo]
  );

  if (isError) {
    return <div>{error.message}</div>;
  }

  return (
    <>
      <form onSubmit={onSubmit}>
        <label>할 일: </label>
        <input
          type="text"
          value={todo}
          onChange={(e) =>
            setTodo(e.target.value)
          }
        />
        <button type="submit">작성</button>
      </form>

      <br />

      <div>
        {isLoading ? (
          <div>Loading...</div>
        ) : (
          todos?.map((todo) => (
            <Fragment key={todo.id}>
              <div>ID: {todo.id}</div>
              <div>할 일: {todo.todo}</div>

              <br />
              <hr />
            </Fragment>
          ))
        )}
      </div>
    </>
  );
};
export default TodosPage;

[ 결과 ]
입력과 동시에 데이터가 리패칭 되어 아래에 나난다.

addTodo -> 성공 -> todos 쿼리 무효화 -> 데이터 리패치

Invalidate Queries 쿼리를 통해 데이터를 리패치 할 경우 이전 데이터를 전부 무효화 시키기 때문에 네트워크 리소스가 낭비될 수 있다.
추가한 데이터만 서버측에서 응답으로 주고, 그것을 화면에 그리면 GET 요청을 하지 않아도 된다.

2) setQueryData

쿼리 무효화 없이 변경된 데이터만 추가 / 수정 / 삭제 해주는 방법이다.

[ 예시 ]

const { mutate } = useMutation(addTodo, {
    onSuccess: (data) => {
      // queryClient.invalidateQueries("todos");
      queryClient.setQueryData("todos", (oldData) => {
        if (!oldData) {
          return [];
        }
        return [...oldData, { id: data.id, todo: data.todo, done: false }];
      });
    },
  });

onSuccess()의 data는 서버에서 응답해준 값이다.
oldData는 todos쿼리가 가지고 있던 기존의 데이터이다. 데이터 불변성을 지키기 위해 ...oldData를 해주어야 한다.

[ 결과 ]

쿼리 무효화와는 다르게 네트워크 낭비 없이 데이터를 추가했다.
이 방법을 사용할 경우에는 서버의 response 데이터가 필요하다.

3) Optimistic Update

위의 두가지 방법은 서버 통신(POST) 후 onSuccess일 경우에만 데이터를 바꾸어주는 경우이다.
이와 반대로 Optimistic Update는 POST 요청 직후 데이터를 바꾸어주고 실패했을 경우 onError, onSettled를 활용해 롤백 시킨다.

[ 예시 ]

const { mutate } = useMutation(addTodo, {
    // ** 1
    onMutate: async (newTodo) => {
      // ** 2
      await queryClient.cancelQueries("todos");

      const previousTodos = queryClient.getQueryData("todos");

      queryClient.setQueryData("todos", (oldData) => {
        if (!oldData) {
          return [];
        }

        return [
          ...oldData,
          { id: oldData.length + 1, todo: newTodo, done: false },
        ];
      });
      return { previousTodos };
    },
    // ** 3
    onError: (_error, _newTodo, context) => {
      queryClient.setQueryData("todos", context?.previousTodos);
    },
    // ** 4
    onSettled: () => {
      queryClient.invalidateQueries("todos");
    },
  });
  1. onMutate는 addTodo함수가 호출되기 전에 실행된다.
  2. cancelQueries는 혹시 모르게 발생하는 refetch를 취소해 Optimistic Update의 데이터를 덮어쓰지 않도록 예방한다.
  3. 에러가 발생했을 경우 이전 데이터로 돌려놓는다.
  4. onSettled는 성공이든, 실패든 실행된다. 즉, 무결성을 위한 작업이다.

[ 결과 ]
요청 즉시 화면을 업데이트 시키기 때문에 사용자 경험이 향상될 수 있다.
하지만 추가 작업이 필요하기 때문에 본인은 사용하지 않는다.

0개의 댓글