Как заставить useSWR отрабатывать только на клиенте, пропуская сервер
Что проверяют
Знаете ли вы, как SWR ведёт себя при серверном рендеринге, и понимаете ли разницу между «компонент рендерится на сервере» и «запрос выполняется на сервере». Следом обычно спрашивают про ошибку гидратации и про то, чем 'use client' отличается от отключения SSR.
Как отвечать
Начните с главного: SWR и так не делает запросов на сервере. Загрузка запускается из эффекта после монтирования, а на сервере эффекты не выполняются, поэтому при SSR хук вернёт data: undefined или значение из fallbackData. Значит, реальная проблема обычно другая: сервер и клиент рендерят разный HTML либо ключ зависит от window, localStorage или токена из браузера.
Рабочие приёмы:
- условный ключ:
useSWR(ready ? '/api/me' : null, fetcher). При ключеnullзапрос не выполняется, это штатный способ отложить загрузку; - флаг монтирования:
useState(false)плюсuseEffect(() => setMounted(true), []), и ключ зависит отmounted. Сервер и первый клиентский рендер совпадают, ошибки гидратации нет; - компонент только для клиента:
dynamic(() => import('./Widget'), { ssr: false })в Next.js. В App Router такой вызов разрешён только внутри клиентского компонента; - данные с сервера: если нужно наоборот показать данные сразу, их передают через
fallbackвSWRConfig, а SWR на клиенте их перепроверит.
Отдельно оговорите: 'use client' не отключает SSR. Такой компонент всё равно пререндерится на сервере в HTML, директива лишь делает его клиентским, с гидратацией и хуками. А при suspense: true на сервере SWR требует начальные данные через fallbackData или fallback.
Где спотыкаются
Делают ранний return по typeof window === 'undefined' перед вызовом useSWR. Это нарушает правила хуков и даёт разный HTML на сервере и клиенте, отсюда ошибка гидратации.
Инициализируют хук с ключом null, а потом вызывают mutate('/api/data'), надеясь сменить ключ. Связанный mutate ключ хука не меняет, запрос так и не уйдёт.
Путают 'use client' с чисто клиентским рендерингом и удивляются, что обращение к window в теле компонента падает при сборке или на сервере.