반응형
React Query는 서버 상태 관리를 위한 라이브러리이다. 데이터 Fetching, Caching, 동기화, 서버 데이터 업데이트 등을 쉽게 만들어 준다.
리액트 쿼리를 사용하는 이유
- 간편한 데이터 관리: 데이터 가져오기, 캐싱, 동기화 및 업데이트 처리를 간편하게 할 수 있다.
- 실시간 업데이트 및 동기화
- 데이터 캐싱: 불필요한 api 요청을 줄인다.
- 서버 상태 관리: 로딩중, 에러, 성공 등의 상태를 간편하게 처리 할 수있다.
- 간편한 설정: @tanstack/react-query @tanstack/react-query-devtools 를 설치 한 후 최상위 컴포넌트에 QueryClientProvider 로 감싸주면 끝난다.
React Query 의 라이프 사이클
- 쿼리 인스턴스가 mount됨
- 네트워크에서 데이터 fetch 하고 query key 로 캐싱함
- 이 데이터는 fresh 상테에서 staleTime(기본값0) 이후 stale 상태로 변경됨
- A 쿼리 인스턴스가 unmount 됨
- 캐시는 chacheTime(기본값 5min) 만큼 유지되다가 가비지 콜렉터로 수집됨.
- 만약, chacheTime 이 지나기 전에 A 쿼리 인스턴드가 새롭게 mount 되면, fetch 가 실행되고 fresh 한 값을 가져오는 동안 캐시 데이터를 보여줌.
Query
function Todos() {
const { isPending, isError, data, error } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodoList,
staleTime: 5000,
retry: 10,
initialData: initialTodos,
})
if (isPending) {
return <span>Loading...</span>
}
if (isError) {
return <span>Error: {error.message}</span>
}
// We can assume by this point that `isSuccess === true`
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}
useQuery({ queryKey, queryFn, [...options] })
- queryKey : 쿼리의 고유 키로, 어플리케이션 전체에서 쿼리를 다시 가져오고, 캐싱하고, 공유하는 데 내부적으로 사용된다.
- 쿼리 키는 가져오는 데이터를 고유하게 설명하므로 쿼리 함수에서 사용하는 모든 변수를 포함해야 한다 .
- 쿼리 키는 객체의 키 순서 관계없이 해시된다.
- queryFn: Promise를 반환하는 모든 함수
- staleTime : 데이터가 한번 fetch 되고 나서 staleTime이 지나지 않았다면 unmount 후 mount 되어도 fetch가 일어나지 않는다.
- cacheTime : cacheTime이 지나기 전에 쿼리 인스턴스가 다시 마운트 되면, 데이터를 fetch하는 동안 캐시 데이터를 보여준다.
- refetchOnWindowFocus : 사용자가 애플리케이션 창을 떠났다가 돌아왔을 경우 자동으로 백그라운드에서 새로운 데이터를 요청한다. 이는 QueryClient 에서도 전역적으로도 사용할 수 있다.
- retry : [ boolean, number, () => boolean | number ] 쿼리가 실해할 경우 재시도 횟수를 설정한다. default = 3
- initialData : 쿼리에 대한 초기 데이터가 이미 있고, 이를 퉈리에 직접 제공하는 경우 사용한다.
- return : 쿼리에 대한 모든 정보가 포함되어있다.
- 오류 발생 및 처리
- 쿼리에 오류가 있는지 확인하려면 거부된 Promise 를 던지거나 반환해야 한다.
- throw되지 않는 클라이언트와 함께 사용하는 경우 스스로 던져야한다. ex) axios, graphql
- 오류 발생 및 처리
useQuery({
queryKey: ['todos', todoId],
queryFn: async () => {
const response = await fetch('/todos/' + todoId)
if (!response.ok) {
throw new Error('Network response was not ok')
}
return response.json()
},
})
Parallel Queries
수동 : 병렬 쿼리 수가 변경되지 않으면 아래와 같이 나란히 나열하여 사용한다.
function App () {
// The following queries will execute in parallel
const usersQuery = useQuery({ queryKey: ['users'], queryFn: fetchUsers })
const teamsQuery = useQuery({ queryKey: ['teams'], queryFn: fetchTeams })
const projectsQuery = useQuery({ queryKey: ['projects'], queryFn: fetchProjects })
...
}
동적 : 실행해야 하는 쿼리 수가 렌더링마다 변경되는 경우 hook 규칙을 위반한다.
원하는 만큼 많은 쿼리를 동적으로, 병렬로 실행하는 경우 useQueries hook 을 사용한다.
function App({ users }) {
const userQueries = useQueries({
queries: users.map((user) => {
return {
queryKey: ['user', user.id],
queryFn: () => fetchUserById(user.id),
}
}),
})
}
Dependent Queries
종속 쿼리는 실행되기 전에 완료하기 위해 이전 쿼리에 의존한다.
유의점으로 종속쿼리 요청은 waterfall 형식으로 성능을 저하시킨다. 대기 시간이 긴 클라이언트에서 발생할 때 특히 해롭나. 가능하면 두 쿼리를 병렬로 가져올 수 있도록 백엔드 API 를 재구성 하는 것이 좋다.
1. useQuery 종속 쿼리
enabled option을 사용하여 이전 쿼리에 의존한다.
// Get the user
const { data: user } = useQuery({
queryKey: ['user', email],
queryFn: getUserByEmail,
})
const userId = user?.id
// Then get the user's projects
const {
status,
fetchStatus,
data: projects,
} = useQuery({
queryKey: ['projects', userId],
queryFn: getProjectsByUser,
// The query will not execute until the userId exists
enabled: !!userId,
})
2. useQueries 종속 쿼리
// Get the users ids
const { data: userIds } = useQuery({
queryKey: ['users'],
queryFn: getUsersData,
select: (users) => users.map((user) => user.id),
})
// Then get the users messages
const usersMessages = useQueries({
queries: userIds
? userIds.map((id) => {
return {
queryKey: ['messages', id],
queryFn: () => getMessagesByUsers(id),
}
})
: [], // if users is undefined, an empty array will be returned
})
Mutation
// This will not work in React 16 and earlier
const CreateTodo = () => {
const mutation = useMutation({
mutationFn: (event) => {
event.preventDefault()
return fetch('/api', new FormData(event.target))
},
})
return <form onSubmit={mutation.mutate}>...</form>
}
// This will work
const CreateTodo = () => {
const mutation = useMutation({
mutationFn: (formData) => {
return fetch('/api', formData)
},
})
const onSubmit = (event) => {
event.preventDefault()
mutation.mutate(new FormData(event.target))
}
return <form onSubmit={onSubmit}>...</form>
}
useMutation({ mutationFn })
const CreateTodo = () => {
const mutation = useMutation({
mutationFn: (formData) => {
return fetch('/api', formData)
},
})
const onSubmit = (event) => {
event.preventDefault()
mutation.mutate(new FormData(event.target))
}
return <form onSubmit={onSubmit}>...</form>
}
'useMutation'은 mutation life cycle 중 쉽게 side effect 를 처리할 수 있는 헬퍼들을 제공한다.
useMutation({
mutationFn: addTodo,
onSuccess: (data, variables, context) => {
// I will fire first
},
onError: (error, variables, context) => {
// I will fire first
},
onSettled: (data, error, variables, context) => {
// I will fire first
},
})
mutate(todo, {
onSuccess: (data, variables, context) => {
// I will fire second!
},
onError: (error, variables, context) => {
// I will fire second!
},
onSettled: (data, error, variables, context) => {
// I will fire second!
},
})
반응형
'React' 카테고리의 다른 글
[React] 렌더링 최적화하는 방법 (0) | 2022.03.10 |
---|---|
[React] CORS 에러 (OPTIONS 요청 발생) (0) | 2021.12.24 |
[React] 리액트 댓글 좋아요 구현하기 (0) | 2021.12.23 |
댓글