공식문서 : https://tanstack.com/query/v4/docs/guides/initial-query-data
캐시에 쿼리의 초기 데이터를 제공할 수 있는 방법에는 여러가지가 있다 :
initialData
제공queryClient.prefetchQuery
를 이용하여 데이터 prefetchqueryClient.setQueryData
를 이용하여 수동으로 데이터를 캐시에 넣기initialData
to prepopulate a query앱에서 사용할 수 있는 쿼리의 초기 데이터가 이미 존재하고 단순히 쿼리에 직접 제공할 수 있는 경우가 있을 수 있다. 이 경우 config.initialData
옵션을 이용하여 쿼리의 초기 데이터를 설정하고 초기 로딩 상태를 건너뛸 수 있다!
중요 :
initialData
는 캐시에 유지되므로 이 옵션에 placeholder, 부분적 또는 불완전한 데이터를 제공하는것은 권장되지 않으며 대신placeholderData
를 사용하는 것이 좋다.
function Todos() {
const result = useQuery(['todos'], () => fetch('/todos'), {
initialData: initialTodos,
})
}
staleTime
and initialDataUpdateTime
기본적으로 initialData
는 방금 fetch된것처럼 완전히 fresh하게 처리된다. 이것은 또한 staleTime
옵션에 의해 해석되는 방식에 영향을 미칠 수 있음을 의미한다.
initialData
로 구성하고, staleTime
을 없게 설정하면(기본 staleTime
: 0), 쿼리는 마운트 시 즉시 refetch된다.function Todos() {
// Will show initialTodos immediately, but also immediately refetch todos after mount
const result = useQuery(['todos'], () => fetch('/todos'), {
initialData: initialTodos,
})
}
initialData
로, staleTime
을 1000ms로 구성하면 데이터는 쿼리 함수에서 방금 fetch된것처럼 동일한 시간 동안 fresh하게 간주된다.function Todos() {
// Show initialTodos immediately, but won't refetch until another interaction event is encountered after 1000 ms
const result = useQuery(['todos'], () => fetch('/todos'), {
initialData: initialTodos,
staleTime: 1000,
})
}
initialData
가 완전히 fresh하지 않으면 어떻게 될까? 이렇게 되면 실제로 가장 정확하고 initialDataUpdatedAt
옵션을 사용하는 마지막 구성이 남는다. 이 옵션을 사용하면 initialData
자체가 마지막으로 업데이트된 시간(예 : Data.now()에서 제공하는 시간)의 numeric JS timestamp를 밀리초 단위로 전달할 수 있다. unix timestamp가 있는 경우 1000
을 곱하여 JS timestamp로 변환해야 한다.function Todos() {
// Show initialTodos immediately, but won't refetch until another interaction event is encountered after 1000 ms
const result = useQuery(['todos'], () => fetch('/todos'), {
initialData: initialTodos,
staleTime: 60 * 1000 // 1 minute
// This could be 10 seconds ago or 10 minutes ago
initialDataUpdatedAt: initialTodosUpdatedTimestamp // eg. 1608412420052
})
}
이 옵션을 사용하면 staleTime
을 원래의 목적으로 사용하여 데이터를 얼마나 fresh하게 유지해야하는지 결정하는 동시에 initialData
가 staleTime
보다 오래된 경우 마운트시 데이터를 refetch할 수 있다. 위의 예시에서, 데이터는 1분 이내로 fresh되어야 하며, initialData
가 마지막으로 업데이트되었을때 쿼리에 힌트를 줄 수 있으므로 쿼리가 데이터를 refetch해야하는지 아닌지를 스스로 결정할 수 있다.
데이터를 prefetch된 데이터로 처리하려면,
prefetchQuery
또는fetchQuery
API를 사용하여 캐시를 미리 채워staleTime
을initialData
와 독립적으로 구성할 수 있도록 하는 것이 좋다.
쿼리의 inital data에 접근하는 프로세스가 집약적이거나 모든 렌더링에서 수행하려는 작업이 아니라면, 함수를 initialData
값으로 전달할 수 있다. 이 함수는 쿼리가 초기화될때 한번만 실행되므로 소중한 메모리 및/또는 CPU를 절약할 수 있다.
function Todos() {
const result = useQuery(['todos'], () => fetch('/todos'), {
initialData: () => {
return getExpensiveTodos()
},
})
}
경우에 따라 다른 쿼리의 캐시된 결과에서 쿼리의 inital data를 제공할 수 있다. 이에 대한 좋은 예는, 개별 todo item에 대한 todo 리스트 쿼리에서 캐시된 데이터를 검색하여 개별 todo 쿼리의 initial data로 사용하는 것이다.
function Todo({ todoId }) {
const result = useQuery(['todo', todoId], () => 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
캐시에서 초기 데이터를 가져오는것은 초기 데이터를 조회하는데 사용하는 소스 쿼리가 오래되었을 가능성이 있지만 initialData
임을 의미한다. 인위적인 staleTime
을 사용하여 쿼리가 즉시 refetch되지 않도록 하는 대신, 소스쿼리의 dataUpdatedAt
을 initialDataUpdatedAt
에 전달하는게 좋다. 이렇게 하면 제공되는 초기 데이터에 관계없이, 쿼리가 refetch되어야하는지 여부와 시기를 결정하는데 필요한 모든 정보를 쿼리 인스턴스에 제공한다.
function Todo({ todoId }) {
const result = useQuery(['todo', todoId], () => fetch(`/todos/${todoId}`), {
initialData: () =>
queryClient.getQueryData(['todos'])?.find(d => d.id === todoId),
initialDataUpdatedAt: () =>
queryClient.getQueryState(['todos'])?.dataUpdatedAt,
})
}
만약 초기 데이터를 조회하는데 사용하는 원본 쿼리가 오래된 경우, 캐시된 데이터를 전혀 사용하지 않고 서버에서 가져오기만 하면 된다. 이 결정을 쉽게 하기 위해서, queryClient.getQueryState
메소드를 사용하여 쿼리가 필요에 맞게 "fresh"한지 결정하는데 사용할 수 있는 state.dataUpdatedAt
timestamp를 포함하여 원본 쿼리에 대한 자세한 정보를 얻을 수 있다.
function Todo({ todoId }) {
const result = useQuery(['todo', todoId], () => 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
를 비교하려면 Community Resources를 확인해라.