๐Ÿต๏ธ React-Query ์ œ๋Œ€๋กœ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ (1) useMutation

Janeยท2023๋…„ 11์›” 5์ผ
18
post-thumbnail

๐Ÿค” useMutation์ด๋ž€?

  • React-Query๋ฅผ ์ด์šฉํ•ด ์„œ๋ฒ„์— ๋ณ€๊ฒฝ(insert, update, delete) ์ž‘์—… ์š”์ฒญ ์‹œ ์‚ฌ์šฉ

๐Ÿง ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€?

mutationFn

  • promise ์ฒ˜๋ฆฌ๊ฐ€ ์ด๋ฃจ์–ด์ง€๋Š” mutation function
  • axios๋ฅผ ์ด์šฉํ•ด ์„œ๋ฒ„์— API๋ฅผ ์š”์ฒญํ•˜๋Š” ๋ถ€๋ถ„
// ์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•
const deleteData = useMutation(() => axios.delete(`api/delete/${id}`));

// ๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•
const deleteData = useMutation({
  mutationFn: (id) => axios.delete(`api/delete/${id}`)
})

mutate

  • useMutation์„ ์ด์šฉํ•ด ์ž‘์„ฑํ•œ ๋‚ด์šฉ๋“ค์ด ์‹ค์ œ๋กœ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š” trigger ์—ญํ• ์„ ํ•จ
  • useMutation์„ ์ •์˜ํ•ด์ค€ ํ›„, ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ
const { mutate } = () => deleteData()

const deleteFn = () => {
	deleteData.mutate(id)
}

onSuccess, onError, onSettled

async/await ๋งŒ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ

const deleteData = async () => {
	try{
		const res = await axios.delete(`api/delete/${id}`)
        .then((res) => console.log(res.data))
	} catch(err) {
		console.error('error', err.message)
	} finally {
		console.log('์–ด์จŒ๋“  ์‹คํ–‰๋˜๋Š” ๋ถ€๋ถ„')
	}
}

useMutation ์ ์šฉ ์‹œ

// fn key ๊ฐ’ ์ƒ๋žต๋œ ๋ฒ„์ „
const deleteData = useMutation((id) => axios.delete(`api/delete/${id}`), {
	onSuccess: () => { console.log('์š”์ฒญ ์„ฑ๊ณต') },
  	onError: () => { console.error('์—๋Ÿฌ ๋ฐœ์ƒ') },
  	onSettled: () => { console.log('๊ฒฐ๊ณผ์— ๊ด€๊ณ„ ์—†์ด ๋ฌด์–ธ๊ฐ€ ์‹คํ–‰๋จ') }
})

// fn key ๊ฐ’ ๋ช…์‹œ๋œ ๋ฒ„์ „
const deleteData = useMutation({
  	mutationFn: (id) => axios.delete(`api/delete/${id}`),
	onSuccess: () => { console.log('์š”์ฒญ ์„ฑ๊ณต') },
  	onError: () => { console.error('์—๋Ÿฌ ๋ฐœ์ƒ') },
  	onSettled: () => { console.log('๊ฒฐ๊ณผ์— ๊ด€๊ณ„ ์—†์ด ๋ฌด์–ธ๊ฐ€ ์‹คํ–‰๋จ') }
})
  • onSuccess, onError, onSettled๋Š” useMutation ์ •์˜ ์‹œ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ mutate์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

๐Ÿ’“ useMutation์œผ๋กœ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ํ•˜๊ธฐ

  • React Query๋Š” ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ์ง€์›ํ•˜๋ฉฐ, ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ๊ฐ€ ์„ฑ๊ณตํ•˜๊ธฐ ์ „์—๋„ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•จ
  • ์„œ๋ฒ„์™€์˜ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™”๋ฅผ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š๊ณ  ๋จผ์ € ์‚ฌ์šฉ์ž์—๊ฒŒ ์„ฑ๊ณต ์‹œ UI๋ฅผ ๋ณด์—ฌ์ค€ ํ›„, ์š”์ฒญ์˜ ๊ฒฐ๊ณผ๊ฐ€ ์˜ค๋ฉด ์„ฑ๊ณต/์‹คํŒจ ์—ฌ๋ถ€์— ๋”ฐ๋ผ UI ์—…๋ฐ์ดํŠธ
  • ์‚ฌ์šฉ์ž๋Š” ์„œ๋ฒ„์™€์˜ ํ†ต์‹  ์—ฌ๋ถ€์™€ ๊ด€๊ณ„ ์—†์ด UI๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋จ
  • ์˜ˆ: ์ธ์Šคํƒ€๊ทธ๋žจ์˜ ์ข‹์•„์š” ๊ธฐ๋Šฅ, ์นด์นด์˜คํ†ก์ด ์ผ๋‹จ ์ „์†ก๋œ ํ›„ ์„ฑ๊ณต, ์ทจ์†Œ/์žฌ์ „์†ก ์ฐฝ์ด ๋‚˜์ค‘์— ๋œจ๋Š” ๊ฒƒ

onMutate

  • React Query์—์„œ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•
  • API Call ์ „์— ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜
  • ์„ฑ๊ณต ์‹œ ํ˜„์žฌ ๋ฐ์ดํ„ฐ ์บ์‹œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ฑฐ๋‚˜ UI๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ , ์‹คํŒจ ์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” rollback ๋งค์ปค๋‹ˆ์ฆ˜๋„ ์ œ๊ณตํ•จ
  • onMutate callback ํ•จ์ˆ˜์—์„œ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ , setQueryData ํ•จ์ˆ˜๋กœ ์ด์ „ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ
  • onError ์ฝœ๋ฐฑํ•จ์ˆ˜์—์„œ ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ์ด์ „ ๋ฐ์ดํ„ฐ๋กœ rollback
const queryClient = useQueryClient(() => axios.post(`api/like/${id}`), {
	onMutate: async (id) => {
      // 'queryKey'๋กœ ์ง„ํ–‰ ์ค‘์ธ refetch ์ทจ์†Œํ•˜์—ฌ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ๋ฎ์–ด์“ฐ์ง€ ์•Š๋„๋ก ํ•จ
		await queryClient.cancleQueries({
        	queryKey: ['queryKey]
        })
          
      // ์ด์ „ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ด
        const previousData = queryClient.getQueryData(['queryKey']);
      
      // ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ
      	queryClient.setQueryData(['queryKey'], (prev) => !prev)
      
      	return { previousData }
    },
  // mutation์ด ์‹คํŒจํ•œ ๊ฒฝ์šฐ
  onError: (err, newData, context) => {
    // onMutate๋กœ๋ถ€ํ„ฐ ๋ฐ˜ํ™˜๋œ context๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ rollback
    queryClient.setQueryData(['queryKey'], context.previousData)
  },
  onSettled: () => {
    // ์„ฑ๊ณต, ์‹คํŒจ ์—ฌ๋ถ€์— ๊ด€๊ณ„ ์—†์ด refetch
   	queryClient.invalidateQueries({queryKey: ['queryKey']}) 
  }
});

const deleteData = useMutation({
 	 
})

๐Ÿ’ก useMutation์œผ๋กœ ์—…๋ฐ์ดํŠธ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜์˜ํ•˜๋ ค๋ฉด?

invalidateQueries

  • useQuery์—์„œ ์‚ฌ์šฉ๋œ queryKey์˜ ์œ ํšจ์„ฑ์„ ์ œ๊ฑฐํ•ด์ค„ ๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉ๋จ
  • queryKey์˜ ์œ ํšจ์„ฑ์„ ์ œ๊ฑฐํ•ด์ฃผ๋Š” ์ด์œ ?
    - ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ์กฐํšŒํ•ด์˜ค๊ธฐ ์œ„ํ•ด์„œ!
  • useQuery์—๋Š” staleTime, cacheTime์ด ์กด์žฌํ•˜์—ฌ ์—…๋ฐ์ดํŠธ ์‚ฌํ•ญ์ด ์ƒ๊ฒผ๋”๋ผ๋„ ํ•ด๋‹น ์‹œ๊ฐ„์ด ์ง€๋‚˜๊ธฐ ์ „๊นŒ์ง€๋Š” ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ™”๋ฉด์— ๋ณด์—ฌ์คŒ
  • invalidateQueries๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ€์ง€๊ณ  ์žˆ๋˜ queryKey์˜ ์œ ํšจ์„ฑ์„ ์ œ๊ฑฐํ•ด์คŒ์œผ๋กœ์จ ์บ์‹ฑ๋˜์–ด ์žˆ๋˜ ๋ฐ์ดํ„ฐ ๋Œ€์‹  ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„์— ์š”์ฒญ
const queryClient = useQueryClient(); // ๋“ฑ๋ก๋œ queryClient๋ฅผ ๊ฐ€์ ธ์˜ด

const deleteData = useMutation((id) => axios.delete(`api/delete/${id}`), {
	onSuccess: () => { 
      console.log('์š”์ฒญ ์„ฑ๊ณต');
      // ์š”์ฒญ ์„ฑ๊ณต ์‹œ ํ•ด๋‹น queryKey ์œ ํšจ์„ฑ ์ œ๊ฑฐ
      queryClient.invalidateQueries('queryKey')
    },
  	onError: () => { console.error('์—๋Ÿฌ ๋ฐœ์ƒ') },
  	onSettled: () => { console.log('๊ฒฐ๊ณผ์— ๊ด€๊ณ„ ์—†์ด ๋ฌด์–ธ๊ฐ€ ์‹คํ–‰๋จ') }
})

๐ŸŒŸ References

๊ณต์‹ ๋ฌธ์„œ
์ฐธ์กฐ ๋ธ”๋กœ๊ทธ 1
์ฐธ์กฐ ๋ธ”๋กœ๊ทธ 2

profile
An investment in knowledge pays the best interest๐Ÿ™ƒ

0๊ฐœ์˜ ๋Œ“๊ธ€