리액트쿼리(v3) 문서- useMutation

hwisaac·2023년 2월 3일
1

리액트쿼리

목록 보기
2/5

mutation

  • query 와 달리 mutation 은 데이터를 create/update/delete 하거나 서버사이드에서 작용시킬때 사용되는게 일반적이다.

예제

 function App() {
   const mutation = useMutation(newTodo => {
     return axios.post('/todos', newTodo)
   })
 
   return (
     <div>
       {mutation.isLoading ? (
         'Adding todo...'
       ) : (
         <>
           {mutation.isError ? (
             <div>An error occurred: {mutation.error.message}</div>
           ) : null}
 
           {mutation.isSuccess ? <div>Todo added!</div> : null}
 
           <button
             onClick={() => {
               mutation.mutate({ id: new Date(), title: 'Do Laundry' })
             }}
           >
             Create Todo
           </button>
         </>
       )}
     </div>
   )
 }
  • 위의 예제에서 mutate 함수를 호출 할 때 single variable or object를 전달하는 것을 알수 있다.
  • 단순히 변수들과 함께 쓰는 것만으로는 mutation 이 특별한 것은 아니다. onSuccess 옵션으로, Query Client 의 invalidateQueries 메소드와 setQueryData 메소드를 함께 사용하면 mutations 는 훨씬 파워풀한 툴이 된다.

주의!

mutate 함수는 비동기함수이다. 따라서, 리액트 16이하 버전에서는 이벤트콜백으로 직접적으로 쓰지 않아야 한다.

onSubmit 이벤트에 접근하고 싶다면 mutate 함수를 다른 함수로 감싸야 한다. ( React event pooling 때문)

 // 리액트16 이하 버전에서는 작동 안함
 const CreateTodo = () => {
   const mutation = useMutation(event => {
     event.preventDefault()
     return fetch('/api', new FormData(event.target))
   })
 
   return <form onSubmit={mutation.mutate}>...</form>
 }
 
 // 이렇게 해야 작동함
 const CreateTodo = () => {
   const mutation = useMutation(formData => {
     return fetch('/api', formData)
   })
   const onSubmit = event => {
     event.preventDefault()
     mutation.mutate(new FormData(event.target))
   }
 
   return <form onSubmit={onSubmit}>...</form>
 }

Resetting Mutation state

  • 뮤테이션 요청의 dataerror 를 제거하고 싶을 때가 있는데, reset 함수를 사용하면 된다.
 const CreateTodo = () => {
   const [title, setTitle] = useState('')
   const mutation = useMutation(createTodo)
 
   const onCreateTodo = e => {
     e.preventDefault()
     mutation.mutate({ title })
   }
 
   return (
     <form onSubmit={onCreateTodo}>
       {mutation.error && (
         <h5 onClick={() => mutation.reset()}>{mutation.error}</h5>
       )}
       <input
         type="text"
         value={title}
         onChange={e => setTitle(e.target.value)}
       />
       <br />
       <button type="submit">Create Todo</button>
     </form>
   )
 }

Mutation Side Effects

  • useMutation 몇가지 헬퍼 옵션이 제공된다. 이를 통해서 mutation가 작동하는 각 스테이지마다 빠르고 쉽게 side-effects를 가능하게 한다.
 useMutation(addTodo, {
   onMutate: variables => {
     // mutation이 작동할 때 로직
 
     // 옵셔널하게 데이터를 포함하는 context를 리턴한다.
     return { id: 1 }
   },
   onError: (error, variables, context) => {
     // 에러 발생시
     console.log(`rolling back optimistic update with id ${context.id}`)
   },
   onSuccess: (data, variables, context) => {
     // 쿼리 성공시
   },
   onSettled: (data, error, variables, context) => {
     // 에러, 성공에 관계 없이 
   },
 })

연속적인 mutations

  • onSuccess, onError, onSettled 콜백들을 사용하는 것과 약간 차이가 있다.
  • mutate 함수에 전달되면, 이 mutations 들은 컴포넌트가 여전히 마운트 되어있는 동안에 한해 딱 한번만 작동한다.
  • 이것은 mutate함수가 호출될 때마다 mutation observer 가 제거되고 매번 다시 구독되기 때문이다.
  • 반면에 useMutation 핸들러들은 모든 mutate 콜을 실행시킨다.

주의

useMutation에 전달되는 mutationFn 는 아마 비동기 함수일건데, 이 경우 mutations 가 종료되는 순서는 호출 순서와 다를 수 있다.

 useMutation(addTodo, {
   onSuccess: (data, error, variables, context) => {
     // 3번 호출될 예정
   },
 })
 
 ['Todo 1', 'Todo 2', 'Todo 3'].forEach((todo) => {
   mutate(todo, {
     onSuccess: (data, error, variables, context) => {
       // 어떤 mutation 이 제일 먼저 resolve 되는지와 관계 없이
       // 마지막 mutation인 Todo 3 에 대해 딱 한번만 실행된다.
     },
   })
 })

Promises

  • resolveerror 를 리턴할 Promise 를 갖고 싶다면 mutate 대신에 mutateAsync 를 사용해라.
const mutation = useMutation(addTodo)
 
 try {
   const todo = await mutation.mutateAsync(todo)
   console.log(todo)
 } catch (error) {
   console.error(error)
 } finally {
   console.log('done')
 }

Persist mutations (mutations 지속)

  • mutation 은 필요에 따라 스토리지에 저장될 수 있고 나중에 실행 될 수 있는데, 이는 hydration 함수를 사용해서 할 수있다.
 const queryClient = new QueryClient()
 
 // "addTodo" mutation 정의하기
 queryClient.setMutationDefaults('addTodo', {
   mutationFn: addTodo,
   onMutate: async (variables) => {
     // todos list 에 대한 현재 쿼리들을 취소한다.
     await queryClient.cancelQueries('todos')
 
     // optimistic todo 생성
     const optimisticTodo = { id: uuid(), title: variables.title }
 
     // todos list 에다 optimistic todo 를 추가 
     queryClient.setQueryData('todos', old => [...old, optimisticTodo])
 
     // optimistic todo 를 담은 context 를 리턴
     return { optimisticTodo }
   },
   onSuccess: (result, variables, context) => {
     // todos list 의 optimistic todo 를 result 로 교체한다.
     queryClient.setQueryData('todos', old => old.map(todo => todo.id === context.optimisticTodo.id ? result : todo))
   },
   onError: (error, variables, context) => {
     // todo list 에서 optimistic todo 를 삭제한다.
     queryClient.setQueryData('todos', old => old.filter(todo => todo.id !== context.optimisticTodo.id))
   },
   retry: 3,
 })
 
 // 컴포넌트에서 mutation 시작 
 const mutation = useMutation('addTodo')
 mutation.mutate({ title: 'title' })
 
 // 만약에 디바이스가 오프라인이 된다거나 해서 mutation 이 멈추게 된다면, 
 // 애플리케이션이 종료될 때 멈춘 mutation 이 dehydrated 될 것이다.
 const state = dehydrate(queryClient)
 
 // 다시 애플리케이션이 작동하면 이 mutation 은 다시 hydrated 된다.
 hydrate(queryClient, state)
 
 // 멈춘 mutation를 다시 시작
 queryClient.resumePausedMutations()

useMutation 훅

https://react-query-v3.tanstack.com/reference/useMutation

 const {
   data,
   error,
   isError,
   isIdle,
   isLoading,
   isPaused,
   isSuccess,
   mutate,
   mutateAsync,
   reset,
   status,
 } = useMutation(mutationFn, {
   mutationKey,
   onError,
   onMutate,
   onSettled,
   onSuccess,
   retry,
   retryDelay,
   useErrorBoundary,
   meta,
 })
 
 mutate(variables, {
   onError,
   onSettled,
   onSuccess,
 })

mutationFn : mutation 함수

  • mutationFn: (variables: TVariables) => Promise<TData>
  • 필수로 넣어야 한다.
  • 비동기적인 작업을 하고 promise를 리턴하는 함수 (일명 mutation 함수)
  • variablesmutationFn 에 넣어서 mutate 시킬 오브젝트이다.

useMutation 옵션

옵션명타입default설명
mutationKeymutationKey: stringmutationKey값은 queryClient.setMutationDefaults 또는 devtools 의 the mutation로 세팅 가능
onError(err: TError, variables: TVariables, context?: TContext) => Promise | voidmutation이 error를 만났을때 작동하는 함수
promise가 리턴되면 await였다가 resolve한다
onMutate(variables: TVariables) => Promise<TContext | void> | TContext | voidmutation함수가 작동하면 동일한 변수들을 인자로 실행될 함수
리턴되는 값은 onSettledonError에 전달된다.
업데이트 최적화에 유용할 수 있다.
onSettled(data: TData, error: TError, variables: TVariables, context?: TContext) => Promise | voidmutation이 성공적으로 fetch하거나 error를 만났을때 실행될 함수
promise가 리턴되면, await 였다가 resolve한다
onSuccess(data: TData, variables: TVariables, context?: TContext) => Promise | voidmutation이 fetch에 성공하면 그 결과를 인자로 받아서 작동한다.
promise 리턴시, await and resolved
retryboolean | number | (failureCount: number, error: TError) => boolean0false면 실패시 재시도 안함.
true면 실패마다 무한재시도
number면 숫자만큼 재시도
retryDelaynumber | (retryAttempt: number, error: TError) => number함수면 retryAttemp는 재시도 횟수이고 리턴한 시간(ms)만큼 지연
예시 : attempt => attempt * 1000
useErrorBoundaryundefined | boolean | (error: TError) => booleanundefined (global query config 에서 useErrorBoundary로 세팅)true면 랜더링도중에 mutation error 를 던지고 가장 가까운 error boundary로 전파한다
false면, 에러바운더리에 에러를 던지는 걸 disable한다.
함수에러를 받아서 error boundary로 그 에러를 나타낼지 여부를 리턴한다.
metaRecord<string, unknown>세팅하면, mutation cache 엔트리에 추가정보를 사용할 수 있다. mutation이 사용가능할때 접근할 수 있다.(예를들어 MutationCacheonError, onSuccess 함수들 )

useMutation 훅의 리턴

변수명타입default설명
mutate(variables?: TVariables, { onSuccess, onSettled, onError }) => voidmutation 함수를 호출하며 variables 를 넘겨준다.
여러 요청을 보낼경우 onSuccess 가 마지막 콜 이후에 작동하게 된다.
dataundefined | unknownundefined쿼리에 의해 마지막에 resolve된 데이터값
errornull | TError쿼리에 대한 에러 오브젝트
isErrorbooleanstatus==="error"
isIdlebooleanstatus==="idle"
isLoadingbooleanstatus==="loading"
isSuccessbooleanstatus==="success"
mutate
mutateAsync(variables: TVariables, { onSuccess, onSettled, onError }) => Promisemutate랑 비슷한데, await할 수 있는 promise를 리턴해줌
reset() => voidmutation internal state를 삭제함 (즉 초기 state로 mutation을 리셋)
statusstring"idle" :mutation함수 작동 전 초기상태
"loading":mutation함수 실행중
"error":마지막 mutation이 에러났는지
"success": 마지막 mutation이 성공했는지

0개의 댓글