Enabled и условное выполнение запросов

Параметр enabled управляет моментом запуска запроса в useQuery. По умолчанию любой запрос TanStack Query выполняется сразу после монтирования компонента. Во многих приложениях такое поведение неудобно или даже опасно:

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

Для подобных сценариев используется условное выполнение запросов через enabled.

Базовый пример:

const query = useQuery({
    queryKey: ['profile'],
    queryFn: fetchProfile,
    enabled: false
})

При enabled: false запрос не будет запускаться автоматически.


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

enabled принимает логическое значение:

enabled: true
enabled: false

Также допускается любое выражение, возвращающее boolean:

enabled: !!userId

Если значение:

  • true — запрос может выполняться;
  • false — запрос находится в неактивном состоянии.

Важно понимать: TanStack Query не удаляет запрос из системы. Query продолжает существовать в кэше, но его автоматическое выполнение блокируется.


Состояние запроса при enabled: false

Когда запрос отключён:

const query = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    enabled: false
})

происходит следующее:

  • queryFn не вызывается;
  • сетевой запрос не выполняется;
  • автоматический refetch не работает;
  • запрос не стартует при фокусе окна;
  • не работают интервальные обновления;
  • не выполняются retry-попытки.

При этом объект query всё равно содержит состояние:

console.log(query.status)
console.log(query.fetchStatus)

Чаще всего значения будут такими:

status: 'pending'
fetchStatus: 'idle'

Это означает:

  • запрос ещё не завершён;
  • сетевой процесс сейчас не выполняется.

Условный запуск через наличие данных

Самый распространённый сценарий — выполнение запроса только после появления идентификатора.

Пример:

const userId = user?.id

const postsQuery = useQuery({
    queryKey: ['posts', userId],
    queryFn: () => fetchPosts(userId),
    enabled: !!userId
})

Пока userId отсутствует:

enabled: false

После появления значения:

enabled: true

TanStack Query автоматически запускает запрос.


Почему enabled важен

Без условного выполнения легко получить ошибки.

Проблемный код:

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

Если userId равен undefined, приложение может отправить запрос:

GET /posts/undefined

или:

GET /posts/null

Результат:

  • лишние запросы;
  • ошибки API;
  • загрязнение кэша;
  • неправильные query keys;
  • лишние retry-попытки.

Правильный вариант:

const postsQuery = useQuery({
    queryKey: ['posts', userId],
    queryFn: () => fetchPosts(userId),
    enabled: !!userId
})

Зависимые запросы

Одна из главных областей применения enabled — dependent queries.

Пример:

  1. Сначала загружается пользователь.
  2. Затем — его проекты.
const userQuery = useQuery({
    queryKey: ['user', email],
    queryFn: () => fetchUser(email)
})

const projectsQuery = useQuery({
    queryKey: ['projects', userQuery.data?.id],
    queryFn: () => fetchProjects(userQuery.data.id),
    enabled: !!userQuery.data?.id
})

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

  1. Выполняется userQuery.
  2. Пока пользователя нет — projectsQuery отключён.
  3. После получения id второй запрос автоматически стартует.

Это один из фундаментальных паттернов TanStack Query.


Цепочки зависимых запросов

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

Пример:

const sessionQuery = useQuery({
    queryKey: ['session'],
    queryFn: fetchSession
})

const profileQuery = useQuery({
    queryKey: ['profile', sessionQuery.data?.userId],
    queryFn: () => fetchProfile(sessionQuery.data.userId),
    enabled: !!sessionQuery.data?.userId
})

const settingsQuery = useQuery({
    queryKey: ['settings', profileQuery.data?.id],
    queryFn: () => fetchSettings(profileQuery.data.id),
    enabled: !!profileQuery.data?.id
})

Каждый следующий запрос ожидает завершения предыдущего.


Ручной запуск запроса

Иногда запрос должен выполняться только по действию пользователя.

Пример:

const query = useQuery({
    queryKey: ['report'],
    queryFn: fetchReport,
    enabled: false
})

Запуск:

<button onCl ick={() => query.refetch()}>
    Загрузить отчёт
</button>

В таком сценарии TanStack Query превращается в управляемый механизм загрузки.


Отличие enabled от условного рендера

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

Пример:

{userId && <Posts />}

Это не всегда удобно.

Проблемы:

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

Часто лучше оставить компонент смонтированным и управлять только запросом:

const query = useQuery({
    queryKey: ['posts', userId],
    queryFn: () => fetchPosts(userId),
    enabled: !!userId
})

enabled и авторизация

Очень частый сценарий — ожидание токена.

const token = authStore.token

const profileQuery = useQuery({
    queryKey: ['profile'],
    queryFn: () => fetchProfile(token),
    enabled: !!token
})

Без этого приложение может отправлять неавторизованные запросы:

401 Unauthorized

Использование нескольких условий

enabled может содержать сложную логику.

Пример:

enabled:
    isAuthenticated &&
    !!userId &&
    !isBlocked &&
    hasPermission

TanStack Query выполнит запрос только при соблюдении всех условий.


Вычисление состояния через функции

Иногда логика становится слишком сложной для inline-выражений.

Плохой вариант:

enabled:
    !!user &&
    !!settings &&
    !isLoading &&
    permissions.includes('admin')

Лучше:

const canLoad = (
    !!user &&
    !!settings &&
    !isLoading &&
    permissions.includes('admin')
)

const query = useQuery({
    queryKey: ['admin'],
    queryFn: fetchAdminData,
    enabled: canLoad
})

Так код становится читаемее.


enabled и пустые строки

Ошибка многих разработчиков:

enabled: userId

Если userId — строка:

''

то поведение может быть неочевидным.

Наиболее безопасный вариант:

enabled: !!userId

Двойное отрицание явно приводит значение к boolean.


enabled и числа

Следует учитывать особенности JavaScript.

Пример:

enabled: !!id

Если:

id = 0

то результат:

false

Хотя 0 может быть валидным идентификатором.

В таком случае лучше писать:

enabled: id !== undefined

или:

enabled: id != null

enabled и параметры формы

Часто запрос запускается только после заполнения формы.

Пример поиска:

const searchQuery = useQuery({
    queryKey: ['search', search],
    queryFn: () => searchUsers(search),
    enabled: search.length > 2
})

Пока строка слишком короткая — запрос не выполняется.


enabled и debounce

Очень распространённый паттерн:

const debouncedSearch = useDebounce(search, 500)

const query = useQuery({
    queryKey: ['search', debouncedSearch],
    queryFn: () => searchUsers(debouncedSearch),
    enabled: debouncedSearch.length > 2
})

Поведение:

  1. Пользователь печатает.
  2. Debounce задерживает обновление.
  3. После паузы выполняется запрос.

Так уменьшается нагрузка на API.


enabled и SSR

При серверном рендеринге условное выполнение особенно важно.

Например:

enabled: typeof window !== 'undefined'

Это позволяет отключить клиентские запросы на сервере.


Повторное включение запроса

Когда enabled меняется:

false → true

TanStack Query автоматически запускает запрос.

Пример:

const [isReady, setIsReady] = useState(false)

const query = useQuery({
    queryKey: ['data'],
    queryFn: fetchData,
    enabled: isReady
})

После:

setIsReady(true)

запрос стартует автоматически.


Отключение уже выполненного запроса

Если запрос уже был выполнен, а затем:

enabled: false

то:

  • данные сохраняются в кэше;
  • query не очищается;
  • UI может продолжать отображать старые данные;
  • новые запросы перестают выполняться.

Пример:

const query = useQuery({
    queryKey: ['profile'],
    queryFn: fetchProfile,
    enabled: isVisible
})

При скрытии блока:

isVisible = false

данные остаются доступными.


enabled не удаляет кэш

Это критически важный момент.

enabled: false

не означает:

cache cleared

Кэш остаётся доступным:

query.data

может содержать старые данные.


Взаимодействие с staleTime

Пример:

const query = useQuery({
    queryKey: ['news'],
    queryFn: fetchNews,
    staleTime: 60000,
    enabled: isOnline
})

Если:

isOnline = false

то запросы не выполняются.

После:

isOnline = true

TanStack Query проверяет свежесть данных:

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

enabled и refetch

Даже при отключённом запросе можно вызвать ручное обновление:

const query = useQuery({
    queryKey: ['stats'],
    queryFn: fetchStats,
    enabled: false
})

Запуск:

await query.refetch()

Это важное отличие:

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

Поведение invalidateQueries

Если query отключён:

enabled: false

и выполняется:

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

то запрос помечается устаревшим, но автоматически не стартует.

Запуск произойдёт позже:

  • при enabled: true;
  • либо через refetch().

Типичная ошибка с queryFn

Плохой пример:

const query = useQuery({
    queryKey: ['user', userId],
    queryFn: fetchUser(userId),
    enabled: !!userId
})

Ошибка в том, что функция вызывается немедленно.

Правильно:

const query = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    enabled: !!userId
})

Комбинация с select

enabled хорошо сочетается с трансформацией данных.

const query = useQuery({
    queryKey: ['products', categoryId],
    queryFn: () => fetchProducts(categoryId),
    enabled: !!categoryId,
    select: data => data.items
})

Пока категория не выбрана — запрос не выполняется.


Lazy queries

TanStack Query официально рассматривает запросы с enabled: false как lazy queries.

Пример:

const query = useQuery({
    queryKey: ['analytics'],
    queryFn: fetchAnalytics,
    enabled: false
})

Запуск происходит только по необходимости.


Когда enabled использовать не стоит

Иногда разработчики чрезмерно усложняют логику.

Плохой пример:

enabled: users.length > 0

если API спокойно возвращает пустой массив.

В таких случаях условное выполнение только усложняет систему.


Практический пример

function UserProjects({ email }) {
    const userQuery = useQuery({
        queryKey: ['user', email],
        queryFn: () => fetchUser(email)
    })

    const projectsQuery = useQuery({
        queryKey: ['projects', userQuery.data?.id],
        queryFn: () => fetchProjects(userQuery.data.id),
        enabled: !!userQuery.data?.id
    })

    if (userQuery.isLoading) {
        return <div>Загрузка пользователя...</div>
    }

    if (projectsQuery.isLoading) {
        return <div>Загрузка проектов...</div>
    }

    return (
        <ul>
            {projectsQuery.data?.map(project => (
                <li key={project.id}>
                    {project.name}
                </li>
            ))}
        </ul>
    )
}

Здесь реализован полноценный dependent query flow:

  1. Получение пользователя.
  2. Ожидание id.
  3. Автоматический запуск второго запроса.
  4. Использование кэширования TanStack Query.