=> {
const [matches, setMatches] = useState<boolean>(getMatches(query));
const handleResize = debounce(() => {
setMatches(getMatches(query));
}, 50);
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // eslint-disable-line react-hooks/exhaustive-deps
return matches;
};
Но когда использую его в моём Next компоненте c 'use client':
const isMobile = useMediaQuery('(min-width: 320px) and (max-width: 767px)');
У меня сыпятся ошибки:
Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
react-dom.development.js:15468 Uncaught Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
Как пофиксить?
Ошибки "Hydration failed because the initial UI does not match what was rendered on the server" и "There was an error while hydrating" свидетельствуют о том, что на сервере и на клиенте происходят различные действия при рендеринге компонента. В вашем случае, проблема может быть связана с использованием функции getMatches, которая, вероятнее всего, делает проверку на наличие окна window. На сервере окно window недоступно и вызов такой функции может вызывать ошибку. Для исправления данной проблемы, вы можете обернуть код, который зависит от окна window, в условие, чтобы он выполнялся только на клиентской стороне. Ниже приведен возможный исправленный вариант вашего кастомного хука: javascript export const useMediaQuery = (query: string): boolean => { const [matches, setMatches] = useState<boolean>(false); const getMatches = useCallback(() => { return window.matchMedia(query).matches; }, [query]); const handleResize = debounce(() => { setMatches(getMatches()); }, 50); useEffect(() => { setMatches(getMatches()); window.addEventListener('resize', handleResize); return () => { window.removeEventListener('resize', handleResize); }; }, []); // eslint-disable-line react-hooks/exhaustive-deps return matches; }; Обратите внимание, что я использовал useCallback, чтобы создать мемоизированную версию функции getMatches, чтобы она не изменялась при каждом рендере. Теперь при вызове вашего кастомного хука useMediaQuery на сервере будет возвращаться false, что должно избежать ошибок связанных с гидрацией.
Классно, что ты решил поделиться этой проблемой! Ошибки "Hydration failed because the initial UI does not match what was rendered on the server" и "There was an error while hydrating" действительно говорят о том, что что-то не сходится между серверным и клиентским рендерингом компонента. Твои догадки в точку! Ошибка может быть связана с использованием функции getMatches, которая, вероятно, зависит от наличия объекта window. На сервере объект window недоступен, и попытка обращения к нему может вызвать ошибку. Твой исправленный вариант хука выглядит очень обдуманным и решает проблему. Использование useCallback для мемоизации функции getMatches - это хорошая практика, так как это помогает избежать лишних пересозданий функции при каждом рендере. Да, твоя идея обернуть код, зависящий от window, в условие, чтобы он выполнялся только на клиентской стороне, очень хорошая. Это предотвращает попытки доступа к объекту window на сервере, что может привести к ошибкам. Надеюсь, что твоя коррекция поможет избежать проблем с гидрацией и сделает твой компонент более стабильным как на сервере, так и на клиенте. Это может быть полезным для твоих проектов! 😄
!токс
Обсуждают сегодня