React Query - useMutation()

gyu0714·2022년 11월 7일
0

Hooks

목록 보기
9/9
post-thumbnail

useMutation

useMutation은 React Query를 이용해 서버에 데이터 변경 잡업을 요청할 때 사용한다.
데이터베이스로 비유하자면 insert, update, delete가 모두 포함된다.

// 1
const savePerson = useMutation(mutationFunction);

// 2
const savePerson = useMutation({
	mutationFunction: mutationFunction
})

mutationFunction?

mutationFunction 은 Promise 처리가 이루어지는 함수이다.
fetch 함수, axios를 이용해서 API를 요청하는 부분이다.

//1
const savePerson = useMutation((person: Iperson) => axios.post('/savePerson, person'));

// 2
const savePerson = useMutation({
    mutationFn: (person: Iperson) => axios.post('/savePerson', person)

mutate

mutate는 useMutation을 이용해 작성한 내용들이 실행될 수 있도록 도와주는 trigger 역할을 한다. 즉, useMutation을 정의 해둔 뒤 이벤트가 발생되었을 때 mutate를 사용해주면 되는거다.

import React, {useState} from 'react';
import {useMutation} from 'react-query';

interface Iperson {
  id: number;
  name: string;
  phone: string;
  age: number;
}

const Mutation = (): JSX.Element => {
  const [person, setPerson] = useState<Iperson>({
    id: 0,
    name: '',
    phone: '',
    age: 0,
  });
  const savePerson = useMutation((person: Iperson) =>
    fetch('http://localhost:8080/person', {
      method: 'POST',
      body: JSON.stringify(person),
    }),
  );

  const onSavePerson = () => {
    savePerson.mutate(person); // 데이터 저장
  };
  return <></>;
};

onSuccess, onError, onSettled

일반적으로 서버에 데이터 변경 요청을 하게 되면 변경 요청이 성공할 경우에 추가적인 액션을 할 수 있도록 코드를 작성하고 한다.
이런 상황은 useMutation을 사용할 때도 동일하게 적용이 된다.

async/ await을 사용할 때는 보통 다음과 같이 결과값이 있는지를 확인한뒤 추가 작업을 수행할 수 있는 코드를 작성한다.

try {
	const res = await axios.post('http://localhost:8080/savePerson', person)
    
    if (res) {
    	console.log('success');
    }
} catch (error) {
	console.log('error' + error);
} finally {
	console.log('finally');
}

하지만 useMutation을 사용하면 다음과 같이 표현해줄 수 있다.

// 1
const savePerson = useMutation((person: Iperson) => axios.post('http://localhost:8080/savePerson', person), {
	onSuccess: () => {
    // 요청이 성공한 경우
    	console.log('onSuccess');
    },
    onError: (error) => {
	// 요청이 실패한 경우
    	console.log(error)
    },
    onSettled: () => {
    // 요청 성공, 실패 여부 관계없이 실행되는 경우
    	console.log('onSettled');
    }
});

onSuccess는 요청이 성공되었을 때 실행되는 구간이다.
onError는 에러가 발생될 경우 실행되는 구간이다.
onSettled는 finally 구문처럼 요청이 성공하든 에러가 발생되든 상관없이 마지막에 실행되는 구간이다.

추가적으로 onSuccess, onError, onSettled는 useMutation을 정의할 때만 사용할 수 있는 것이 아니라 mutate에서도 사용이 가능하다.

const onSavePerson = () => {
    savePerson.mutate(person, {
        onSuccess: () => { // 요청이 성공한 경우
            console.log('onSuccess');
        },
        onError: (error) => { // 요청에 에러가 발생된 경우
            console.log('onError');
        },
        onSettled: () => { // 요청이 성공하든, 에러가 발생되든 실행하고 싶은 경우
            console.log('onSettled');
        }
    }); // 데이터 저장
}

invalidateQueries

invalidateQueries는 useQuery에서 사용되는 queryKey의 유효성을 제거해주는 목적으로 사용된다.
그리고 queryKey의 유혀성을 제거해주는 이뉴는 서버로 부터 다시 데이터를 조회해오기 위함이다.
일반적인 생각으로는 이름, 전화번호, 나이를 적은 뒤 저장 버튼을 누르게 되면 리스트가 아래에 바로 표현되는 것을 생각할 수 있다.
하지만 useQuery에는 staleTime과 cacheTime이라는 개념이 존재한다.
정해진 시간이 도달하지 않으면 새로운 데이터가 적재되었더라도 useQuery는 변동 없이 동일한 데이터를 화면에 보여줄 것이다.
결국 사용자 입장에서는 데이터 생성이 제대로 되었는지에 대한 파악이 힘들기 때문에 혼란을 겪을 수 있게 된다.

해당 상황을 해결해줄 수 있는 것이 바로 invalidateQueries이다.
데이터를 저장할 때 invalidateQueries를 이용해 useQuery가 가지고 있던 queryKey의 유효성을 제거해주면 캐싱되어있는 데이터를 화면에 보여주지 않고 서버에 새롭게 데이터를 요청하게 된다.

결국 데이터가 새롭게 추가되었을 때 다시 서버에서 데이터를 가져오게 되면서 추가한 데이터까지 화면에서 확인할 수 있게 된다.

사용 방법은 우선 hook을 이용해 등록했던 queryClient를 가져와야 한다.

그리고 원하는 장소에서 사용해주면 되는데, 일반적으로 요청이 성공했을 때 queryKey의 유효성을 제거할 것이기 때문에 다음과 같이 코드를 작성해줄 수 있다.

cosnt queryClient = useQueryClient();

const savePerson = useMutation((person: Iperson) => axios.post('http://localhost:8080/savePerson', perseon) {
	onSuccess: () => {
    	console.log('onSuccess');
        queryClient.invalidateQueries('person'); // queryKey 유효성 제거
    },
    onError: (error) => {
    	console.log(error)
    },
    onSettled: () => {
    	console.log('onSettled');
    }
});

참고로 여기서 invalidateQueries안에 'person' 이라는 값이 들어가있다.
그 이유는 위에 링크를 걸어둔 useQuery 글에서 useQuery를 사용할 때 'person'이라는 queryKey를 가지고 person 데이터를 화면에 뿌려주고 있기 때문이다.

setQueryData

invalidateQueries를 사용하지 않고도 데이터를 업데이트 해줄 수 있는 방법은 있다.
setQueryData를 활용하면 되는데 setQueryData는 기존에 queryKey에 매핑되어 있는 데이터를 새롭게 정의해준다.

const queryClient = useQueryClient();

const savePerson = useMutation((person: Iperson) => axios.post('http://localhost:8080/savePerson', person), {
	onSuccess: () => {
    	console.log('onSuccess');
        queryClient.setQueryData('person', (data) => {
        	const curPersons = data as AxiosResponse<any, any>;
            curPersons.data.push(perseon);
            
            return curPersons;
        })
    },
    
    onError: (error) => {
    	console.log(error);
    },
    
    onSettled: () => {
    	console.log('onSettled');
    }
});

코드를 이렇게 작성해주면 서버에 다시 person 데이터를 요청하지 않고도 사용자 화면에 변경된 데이터를 함께 보여줄 수 있다.

profile
gyu0714

0개의 댓글