Методы setQueryData и getQueryData

Методы setQueryData и getQueryData являются частью объекта QueryClient и используются для прямой работы с кешем TanStack Query без выполнения сетевых запросов.

Они позволяют:

  • читать данные из кеша;
  • изменять кеш вручную;
  • синхронизировать интерфейс без повторного запроса;
  • обновлять локальное состояние после мутаций;
  • реализовывать optimistic updates;
  • мгновенно отображать данные между страницами и компонентами.

Оба метода работают напрямую с Query Cache и используют queryKey для поиска нужного запроса.


Метод getQueryData

Основное назначение

Метод getQueryData извлекает данные конкретного запроса из кеша.

Синтаксис:

const data = queryClient.getQueryData(queryKey)

Пример:

const user = queryClient.getQueryData(['user', 5])

console.log(user)

Если запрос присутствует в кеше — метод вернёт данные.

Если данных нет — будет возвращён undefined.


Как работает getQueryData

TanStack Query хранит все запросы внутри внутреннего Query Cache.

Каждый запрос идентифицируется по queryKey.

Например:

['posts']

или:

['user', 15]

Метод ищет точное совпадение ключа и возвращает сохранённое значение.


Получение данных без запроса на сервер

Главное преимущество getQueryData — отсутствие сетевого запроса.

Например:

const cachedPosts = queryClient.getQueryData(['posts'])

Здесь:

  • не вызывается fetch;
  • не запускается queryFn;
  • не изменяется состояние запроса;
  • данные читаются мгновенно.

Это особенно важно для:

  • навигации между страницами;
  • повторного использования данных;
  • предотвращения лишних запросов;
  • моментального отображения интерфейса.

Пример с useQuery

const posts = useQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts
})

После успешной загрузки:

const cachedPosts = queryClient.getQueryData(['posts'])

вернёт:

[
    {
        id: 1,
        title: 'Post 1'
    },
    {
        id: 2,
        title: 'Post 2'
    }
]

Проверка наличия кеша

Частый сценарий:

const cachedUser = queryClient.getQueryData(['user', userId])

if (cachedUser) {
    return cachedUser
}

Такой подход позволяет:

  • избежать повторного запроса;
  • использовать данные мгновенно;
  • ускорить интерфейс.

Использование getQueryData внутри мутаций

После выполнения мутации часто требуется получить текущее состояние кеша.

Пример:

const previousTodos = queryClient.getQueryData(['todos'])

Это особенно полезно для rollback при optimistic updates.


Получение данных в обработчике onMutate

const mutation = useMutation({
    mutationFn: updateTodo,

    onMutate: async (newTodo) => {
        await queryClient.cancelQueries({
            queryKey: ['todos']
        })

        const previousTodos =
            queryClient.getQueryData(['todos'])

        return { previousTodos }
    }
})

Здесь:

  • запросы отменяются;
  • текущее состояние кеша сохраняется;
  • данные используются для возможного отката.

Использование getQueryData вне React-компонентов

Метод работает не только внутри компонентов.

Например:

const user = queryClient.getQueryData(['auth'])

можно вызывать:

  • в сервисах;
  • в middleware;
  • в обработчиках роутинга;
  • в utility-функциях.

Метод setQueryData

Основное назначение

Метод setQueryData изменяет данные в кеше вручную.

Синтаксис:

queryClient.setQueryData(queryKey, data)

Пример:

queryClient.setQueryData(
    ['user', 5],
    {
        id: 5,
        name: 'Alex'
    }
)

После вызова:

  • кеш обновляется;
  • подписанные компоненты автоматически перерисовываются;
  • запрос считается актуальным.

Как работает setQueryData

Метод:

  1. Ищет запрос по queryKey;
  2. Обновляет данные;
  3. Уведомляет всех observers;
  4. Запускает ререндер компонентов.

Если запроса ещё нет — TanStack Query создаст его в кеше.


Создание кеша вручную

Даже без useQuery можно записать данные:

queryClient.setQueryData(
    ['settings'],
    {
        theme: 'dark'
    }
)

После этого:

const settings =
    queryClient.getQueryData(['settings'])

вернёт:

{
    theme: 'dark'
}

Обновление кеша после мутации

Один из самых распространённых сценариев.

Без setQueryData

Обычно после мутации выполняется:

queryClient.invalidateQueries({
    queryKey: ['todos']
})

Это запускает новый запрос.


С setQueryData

Можно обновить кеш мгновенно:

queryClient.setQueryData(
    ['todos'],
    (oldData) => {
        return [
            ...oldData,
            newTodo
        ]
    }
)

Преимущества:

  • отсутствие повторного запроса;
  • мгновенное обновление интерфейса;
  • снижение нагрузки на сервер.

Callback-версия setQueryData

Метод поддерживает updater-функцию.

Синтаксис:

queryClient.setQueryData(
    queryKey,
    (oldData) => newData
)

Пример:

queryClient.setQueryData(
    ['counter'],
    (old) => old + 1
)

TanStack Query передаст текущее значение кеша в old.


Почему updater-функция лучше прямого значения

Неправильно:

queryClient.setQueryData(
    ['todos'],
    [...todos, newTodo]
)

Проблема:

  • todos может быть устаревшим;
  • возможна потеря данных;
  • возникает race condition.

Безопаснее:

queryClient.setQueryData(
    ['todos'],
    (old = []) => {
        return [...old, newTodo]
    }
)

Обновление конкретного элемента списка

Пример:

queryClient.setQueryData(
    ['todos'],
    (oldTodos) => {
        return oldTodos.map((todo) => {
            if (todo.id === updatedTodo.id) {
                return updatedTodo
            }

            return todo
        })
    }
)

Такой подход позволяет:

  • избежать повторной загрузки;
  • обновить только нужный элемент;
  • сохранить отзывчивость интерфейса.

Удаление элемента из кеша

queryClient.setQueryData(
    ['todos'],
    (oldTodos) => {
        return oldTodos.filter(
            (todo) => todo.id !== deletedId
        )
    }
)

Обновление объекта

queryClient.setQueryData(
    ['profile'],
    (oldProfile) => {
        return {
            ...oldProfile,
            avatar: newAvatar
        }
    }
)

Работа с undefined

Если кеш отсутствует:

const data =
    queryClient.getQueryData(['todos'])

вернёт:

undefined

Поэтому важно учитывать этот случай:

queryClient.setQueryData(
    ['todos'],
    (old = []) => {
        return [...old, newTodo]
    }
)

Иммутабельность данных

TanStack Query ожидает иммутабельное обновление.

Неправильно:

queryClient.setQueryData(
    ['todos'],
    (oldTodos) => {
        oldTodos.push(newTodo)

        return oldTodos
    }
)

Проблемы:

  • ссылка не меняется;
  • ререндер может не произойти;
  • кеш становится непредсказуемым.

Правильно:

queryClient.setQueryData(
    ['todos'],
    (oldTodos = []) => {
        return [...oldTodos, newTodo]
    }
)

Использование setQueryData в optimistic updates

Это один из ключевых сценариев.

Пример:

const mutation = useMutation({
    mutationFn: addTodo,

    onMutate: async (newTodo) => {
        await queryClient.cancelQueries({
            queryKey: ['todos']
        })

        const previousTodos =
            queryClient.getQueryData(['todos'])

        queryClient.setQueryData(
            ['todos'],
            (old = []) => {
                return [...old, newTodo]
            }
        )

        return { previousTodos }
    },

    onError: (error, newTodo, context) => {
        queryClient.setQueryData(
            ['todos'],
            context.previousTodos
        )
    },

    onSettled: () => {
        queryClient.invalidateQueries({
            queryKey: ['todos']
        })
    }
})

Последовательность работы:

  1. Сохраняется старый кеш;
  2. Интерфейс обновляется мгновенно;
  3. При ошибке выполняется rollback;
  4. После завершения данные синхронизируются с сервером.

setQueryData и invalidateQueries

Методы часто используются вместе.

setQueryData

Используется для:

  • мгновенного обновления интерфейса;
  • локального изменения данных;
  • optimistic updates.

invalidateQueries

Используется для:

  • повторной синхронизации с сервером;
  • проверки актуальности данных;
  • обновления после мутаций.

Комбинация методов

Частая схема:

queryClient.setQueryData(
    ['post', id],
    updatedPost
)

queryClient.invalidateQueries({
    queryKey: ['post', id]
})

Сначала интерфейс обновляется мгновенно, затем сервер подтверждает актуальные данные.


Отличие setQueryData от refetch

setQueryData

  • не делает HTTP-запрос;
  • обновляет локальный кеш;
  • работает мгновенно.

refetch

  • вызывает queryFn;
  • делает сетевой запрос;
  • получает данные с сервера.

Синхронизация между страницами

Допустим, список пользователей уже загружен:

['users']

При переходе на страницу пользователя можно заранее записать данные:

queryClient.setQueryData(
    ['user', user.id],
    user
)

Тогда детальная страница:

useQuery({
    queryKey: ['user', userId],
    queryFn: fetchUser
})

сразу покажет кешированные данные.


Предзаполнение кеша

Частый сценарий:

queryClient.setQueryData(
    ['post', post.id],
    post
)

Это позволяет:

  • избежать пустого состояния;
  • убрать loading;
  • ускорить переходы между страницами.

Работа с вложенными структурами

Пример сложного обновления:

queryClient.setQueryData(
    ['posts'],
    (oldPosts = []) => {
        return oldPosts.map((post) => {
            if (post.id !== postId) {
                return post
            }

            return {
                ...post,
                comments: [
                    ...post.comments,
                    newComment
                ]
            }
        })
    }
)

Производительность

setQueryData обновляет только подписанные запросы.

TanStack Query:

  • сравнивает ссылки;
  • уведомляет observers;
  • минимизирует ререндеры.

Поэтому иммутабельность критически важна.


Частые ошибки

Мутация исходного объекта

Плохо:

oldUser.name = 'John'

return oldUser

Отсутствие проверки undefined

Плохо:

return [...old, item]

Если old === undefined, приложение упадёт.


Использование устаревших данных

Плохо:

setQueryData(['todos'], todos)

Лучше:

setQueryData(
    ['todos'],
    (old) => old
)

Когда использовать getQueryData

Метод подходит для:

  • чтения кеша;
  • проверки наличия данных;
  • rollback;
  • получения локального состояния;
  • доступа к данным без запроса.

Когда использовать setQueryData

Метод подходит для:

  • обновления кеша после мутаций;
  • optimistic updates;
  • синхронизации интерфейса;
  • предзаполнения данных;
  • локального редактирования кеша;
  • мгновенных UI-обновлений.