초기 쿼리 데이터
쿼리가 필요해지기 전에 캐시에 초기 데이터를 제공하는 방법은 다양합니다:
- 선언형으로:
- 캐시가 비어 있는 경우 미리 채우려면 쿼리에
initialData를 제공합니다
- 캐시가 비어 있는 경우 미리 채우려면 쿼리에
- 명령형으로:
queryClient.query를 사용하여 데이터를 프리페치합니다queryClient.setQueryData를 사용하여 데이터를 캐시에 수동으로 넣습니다
initialData를 사용하여 쿼리 미리 채우기
앱에서 쿼리의 초기 데이터를 이미 사용할 수 있어 이를 쿼리에 바로 제공할 수 있는 경우가 있습니다. 이 경우 config.initialData 옵션을 사용하여 쿼리의 초기 데이터를 설정하고 초기 로딩 상태를 건너뛸 수 있습니다!
중요:
initialData는 캐시에 영구 저장되므로, 이 옵션에는 자리표시자 데이터나 일부 또는 불완전한 데이터를 제공하지 말고 대신placeholderData를 사용하는 것이 좋습니다
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: initialTodos,
})
staleTime 및 initialDataUpdatedAt
기본적으로 initialData는 방금 가져온 것처럼 완전히 최신 상태로 취급됩니다. 이는 staleTime 옵션에서 해석되는 방식에도 영향을 줍니다.
-
쿼리 옵저버를
initialData로 구성하고staleTime(기본값은staleTime: 0)를 지정하지 않으면 쿼리가 마운트될 때 즉시 다시 가져옵니다:// Will show initialTodos immediately, but also immediately refetch todos after mount
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: initialTodos,
}) -
쿼리 observer를
initialData및1000ms의staleTime으로 구성하면, 데이터는 쿼리 함수에서 방금 가져온 것처럼 동일한 시간 동안 fresh 상태로 간주됩니다.// Show initialTodos immediately, but won't refetch until another interaction event is encountered after 1000 ms
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: initialTodos,
staleTime: 1000,
}) -
그렇다면
initialData가 완전히 최신 상태가 아니라면 어떻게 해야 할까요? 이제 실제로 가장 정확하며initialDataUpdatedAt이라는 옵션을 사용하는 마지막 구성이 남습니다. 이 옵션을 사용하면 initialData 자체가 마지막으로 업데이트된 시점의 숫자형 JS 타임스탬프를 밀리초 단위로 전달할 수 있으며, 예를 들어Date.now()에서 이를 제공합니다. unix 타임스탬프가 있다면1000을 곱해 JS 타임스탬프로 변환해야 한다는 점에 유의하세요.// Show initialTodos immediately, but won't refetch until another interaction event is encountered after 1000 ms
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: initialTodos,
staleTime: 60 * 1000, // 1 minute
// This could be 10 seconds ago or 10 minutes ago
initialDataUpdatedAt: initialTodosUpdatedTimestamp, // eg. 1608412420052
})이 옵션을 사용하면 staleTime을 데이터가 얼마나 최신이어야 하는지 판단하는 본래 용도로 사용하는 동시에,
initialData가staleTime보다 오래된 경우 마운트 시 데이터를 다시 가져올 수 있습니다. 위 예시에서는 데이터가 1분 이내의 최신 상태여야 하며, initialData가 마지막으로 업데이트된 시점을 쿼리에 알려 쿼리가 데이터를 다시 가져와야 하는지 자체적으로 판단하게 할 수 있습니다.데이터를 프리페치된 데이터로 취급하려면
queryAPI를 사용해 캐시를 미리 채우는 것이 좋습니다. 그러면initialData와 독립적으로staleTime을 구성할 수 있습니다.
초기 데이터 함수
쿼리의 초기 데이터에 접근하는 과정이 집약적이거나 렌더링할 때마다 수행하고 싶지 않은 작업이라면, initialData 값으로 함수를 전달할 수 있습니다. 이 함수는 쿼리가 초기화될 때 한 번만 실행되어 귀중한 메모리 및/또는 CPU를 절약합니다:
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: () => getExpensiveTodos(),
})
캐시의 초기 데이터
일부 상황에서는 다른 쿼리의 캐시된 결과로 쿼리의 초기 데이터를 제공할 수 있습니다. 좋은 예로 todos 목록 쿼리의 캐시된 데이터에서 개별 todo 항목을 검색한 다음, 이를 개별 todo 쿼리의 초기 데이터로 사용하는 경우가 있습니다:
const result = useQuery({
queryKey: ['todo', todoId],
queryFn: () => fetch('/todos'),
initialData: () => {
// Use a todo from the 'todos' query as the initial data for this todo query
return queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId)
},
})
initialDataUpdatedAt을 사용한 캐시의 초기 데이터
캐시에서 초기 데이터를 가져온다는 것은 초기 데이터를 조회하는 데 사용하는 소스 쿼리가 오래되었을 가능성이 높다는 의미입니다. 쿼리가 즉시 다시 가져오기를 수행하지 않도록 인위적인 staleTime을 사용하는 대신, 소스 쿼리의 dataUpdatedAt을 initialDataUpdatedAt에 전달하는 것이 좋습니다. 이렇게 하면 초기 데이터 제공 여부와 관계없이 쿼리를 다시 가져와야 하는지와 그 시점을 판단하는 데 필요한 모든 정보가 쿼리 인스턴스에 제공됩니다.
const result = useQuery({
queryKey: ['todos', todoId],
queryFn: () => fetch(`/todos/${todoId}`),
initialData: () =>
queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId),
initialDataUpdatedAt: () =>
queryClient.getQueryState(['todos'])?.dataUpdatedAt,
})
캐시에서 조건부 초기 데이터 가져오기
초기 데이터를 조회하는 데 사용하는 소스 쿼리가 오래되었다면 캐시된 데이터를 전혀 사용하지 않고 서버에서 바로 가져오고 싶을 수 있습니다. 이 결정을 더 쉽게 내리려면 queryClient.getQueryState 메서드를 대신 사용하여 소스 쿼리에 관한 추가 정보를 얻을 수 있으며, 여기에는 쿼리가 필요에 맞을 만큼 "fresh"한지 판단하는 데 사용할 수 있는 state.dataUpdatedAt 타임스탬프도 포함됩니다:
const result = useQuery({
queryKey: ['todo', todoId],
queryFn: () => fetch(`/todos/${todoId}`),
initialData: () => {
// Get the query state
const state = queryClient.getQueryState(['todos'])
// If the query exists and has data that is no older than 10 seconds...
if (state && Date.now() - state.dataUpdatedAt <= 10 * 1000) {
// return the individual todo
return state.data.find((d) => d.id === todoId)
}
// Otherwise, return undefined and let it fetch from a hard loading state!
},
})
추가 자료
Initial Data와 Placeholder Data의 비교는 TkDodo의 문서를 참조하세요.