Я использую react-static для создания статического веб-сайта. Используя useLayoutEffect из новый хук API, я получаю это предупреждение на этапе статического рендеринга (тот же API, что и рендеринг на стороне сервера):
Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format.
This will lead to a mismatch between the initial, non-hydrated UI and th e intended UI.
To avoid this, useLayoutEffect should only be used in components that render exclusively on the client.
Конечно, это имеет смысл. Но как избавиться от этого предупреждения, если вы уверены, что не будет никакого несоответствия?
В моем эффекте макета я лишь добавляю немного CSS к тегу body, поэтому не будет никаких несоответствий на этапе гидратации на клиенте (поскольку body не является делом React).
React строго запрещает использование условных хуков, но в этом конкретном случае очень не имеет смысла сделать что-то вроде:
if (typeof window !== 'undefined')
useLayoutEffect(() => {
document.body.style.overflowY = loading ? 'hidden' : 'visible'
},
[loading]
)
Каков правильный путь?





Я думаю, что вы должны использовать контекст для передачи значения «скрытый/видимый» другим компонентам. В данном случае компонент, который отображает оболочку всей страницы.
useEffect(() => {
fnSetContextValue(isLoading ? 'hidden' : 'visible')
}, [isLoading]);
Вы также можете попробовать использовать функцию запросAnimationFrame вместо контекста:
useEffect(() => {
window.requestAnimationFrame(() => {
document.body.style.overflowY = loading ? 'hidden' : 'visible';
});
}, [isLoading]);
О, я написал useEffect в своем посте, правда. Исправленный. Я имел в виду useLayoutEffect.
Итак, вот решение не очень грязный, которое я придумал. Вместо реализации наивного решения, т.е. условный хук:
const Layout = () => {
const [loading, setLoading] = useState()
if (typeof window !== 'undefined')
useLayoutEffect(() => {
document.body.style.overflowY = loading ? 'hidden' : 'visible'
}, [loading])
return ( ... )
}
export default Layout
который кажется грязным, антишаблонным, семантически неверным и во многих случаях бесполезным (зачем проверять window при каждом рендеринге?), я просто помещаю условие вне компонента:
const LayoutView = ({ loading, setLoading }) => ( ... )
const Layout = (typeof window === 'undefined') ? (
() => {
const [loading, setLoading] = useState()
return <LayoutView loading = {loading} setLoading = {setLoading}/>
}
): (
() => {
const [loading, setLoading] = useState()
useLayoutEffect(() => {
document.body.style.overflowY = loading ? 'hidden' : 'visible'
}, [loading])
return <LayoutView loading = {loading} setLoading = {setLoading}/>
}
)
export default Layout
Однако будьте осторожны, это работает только потому, что мой эффект макета не влияет на часть React DOM, в чем и был весь смысл предупреждения.
Использование
useEffectвместоuseLayoutEffectдействительно не вызывает предупреждение, но тогда я теряю синхронный аспектuseLayoutEffect.