[TanStackQuery] Initial Query Data

Jeris·2023년 5월 22일
0

쿼리에 필요한 초기 데이터를 캐시에 제공하기 전에 여러 가지 방법으로 제공할 수 있습니다:

initialData를 사용하여 쿼리 미리 채우기

앱에서 쿼리에 대한 초기 데이터를 이미 사용할 수 있고 쿼리에 직접 제공할 수 있는 경우가 있을 수 있습니다. 이 경우 config.initialData 옵션을 사용하여 쿼리의 초기 데이터를 설정하고 초기 로딩 상태를 건너뛸 수 있습니다!

"IMPORTANT: initialData는 캐시에 지속되므로 이 옵션에 자리 placeholder, 부분 데이터 또는 불완전한 데이터를 제공하지 않는 것이 좋으며 대신 placeholderData를 사용하는 것이 좋습니다."

const result = useQuery({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: initialTodos,
})

staleTime and initialDataUpdatedAt

기본적으로 initialData는 방금 가져온 것처럼 완전히 새로운 것으로 취급됩니다. 이는 또한 staleTime 옵션에 의해 해석되는 방식에 영향을 미친다는 것을 의미합니다.

  • query observer를 initialData로 구성하고 staleTime을 사용하지 않는 경우(default staleTime: 0), 쿼리가 마운트될 때 즉시 refetch합니다:

    // Will show initialTodos immediately, but also immediately refetch todos after mount
    const result = useQuery({
      queryKey: ['todos'],
      queryFn: () => fetch('/todos'),
      initialData: initialTodos,
    })
    
  • query observer를 initialData로 구성하고 staleTime1000ms로 설정하여 쿼리 옵저버를 구성하면 쿼리 함수에서 방금 fetched 된 것처럼 같은 시간 동안 데이터가 최신으로 간주됩니다.

    // 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가 완전히 최신이 아니라면 어떻게 해야 할까요? 그러면 실제로 가장 정확한 마지막 구성이 남게 되는데, initialDateUpdatedAt 옵션을 사용합니다. 이 옵션을 사용하면 초기 데이터 자체가 마지막으로 업데이트된 시간(밀리초)의 숫자 JS 타임스탬프를 전달할 수 있습니다(예: Date.now()가 제공하는 것). 유닉스 타임스탬프가 있는 경우 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을 원래의 용도로 사용하여 데이터가 얼마나 최신이어야 하는지를 결정하는 동시에, initialDatastaleTime보다 오래된 경우 마운트 시 데이터를 refetched 되게할 수 있습니다. 위의 예제에서는 데이터가 1분 이내에 새로 고쳐져야 하며, 초기 데이터가 마지막으로 업데이트된 시점을 쿼리에 힌트하여 쿼리가 데이터를 refetched 되어야 하는지 여부를 스스로 결정할 수 있도록 합니다.

    "데이터를 prefetched 데이터로 취급하고 싶다면, prefetchQuery 또는 fetchQuery API들을 사용하여 캐시를 미리 채우고 초기 데이터와 독립적으로 staleTime을 구성할 것을 권장합니다."

Initial Data Function

쿼리의 초기 데이터에 접근하는 과정이 intensive하거나 모든 렌더링에서 수행되게 하지 않으려는 경우 함수를 initialData 값으로 전달할 수 있습니다. 이 함수는 쿼리가 초기화될 때 한 번만 실행되므로 귀중한 메모리 그리고/또는 CPU를 절약할 수 있습니다:

const result = useQuery({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: () => getExpensiveTodos(),
})

Initial Data from Cache

어떤 상황에서는 다른 쿼리의 캐시된 결과에서 쿼리의 초기 데이터를 제공할 수 있습니다. 예를 들어 할 일 목록 쿼리에서 캐시된 데이터를 검색하여 개별 할일 항목을 찾은 다음, 이를 개별 할일 쿼리의 초기 데이터로 사용하는 것이 좋은 예입니다:

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)
  },
})

Initial Data from the cache with initialDataUpdatedAt

캐시에서 초기 데이터를 가져온다는 것은 초기 데이터를 조회하는 데 사용하는 소스 쿼리가 오래되었을 가능성이 높지만 initialData임을 의미합니다. 쿼리를 즉시 refetching하는 것을 막기 위해 인위적인 staleTime을 사용하는 대신, 소스 쿼리의 dataUpdatedAtinitialDataUpdatedAt에 전달하는 것이 좋습니다. 이렇게 하면 제공되는 초기 데이터에 관계없이 쿼리를 다시 가져와야 하는지 여부와 시기를 결정하는 데 필요한 모든 정보를 쿼리 인스턴스에 제공합니다.

const result = useQuery({
  queryKey: ['todos', todoId],
  queryFn: () => fetch(`/todos/${todoId}`),
  initialData: () =>
    queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId),
  initialDataUpdatedAt: () =>
    queryClient.getQueryState(['todos'])?.dataUpdatedAt,
})

Conditional Initial Data from Cache

초기 데이터를 조회하는 데 사용하는 소스 쿼리가 오래된 경우 캐시된 데이터를 전혀 사용하지 않고 서버에서 가져오는 것이 좋을 수 있습니다. 이 결정을 더 쉽게 내릴 수 있도록, 쿼리가 필요에 따라 충분히 '최신(fresh)'인지 판단하는 데 사용할 수 있는 state.dataUpdatedAt 타임스탬프를 포함하여 소스 쿼리에 대한 자세한 정보를 얻기 위해 대신 queryClient.getQueryState 메서드를 사용할 수 있습니다:

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!
  },
})

Further reading

Initial DataPlaceholder Data를 비교하려면 커뮤니티 리소스를 참조하세요.

Reference

profile
job's done

0개의 댓글