https://t.me/super_oleg_dev/102
Ленивая гидрация позволяет не выполнять реакту код компонента на клиенте сразу при гидрации всего приложения, и отлично комбинируется с IntersectionObserver - можно выполнять код только при попадании компонента в область видимости.
Также, можно вообще не выполнять код на клиенте пометив блок как статичный, и каким-либо образом убирать его из клиентского бандла.
Механизм достаточно простой, это по сути легализованный хак в React, детали есть в этом issue - facebook/react#10923 (comment)
Код для lazy обертки может базово выглядеть так:
const LazyRender = ({ children }) => {
const containerRef = useRef(null);
const isVisible = useObserver(containerRef);
if (isVisible) {
return React.createElement('div', {}, children);
}
return React.createElement('div', {
ref: containerRef,
suppressHydrationWarning: true,
dangerouslySetInnerHTML: { __html: '' },
});
};
Абстрактный хук useObserver
должен вернуть true
всегда на сервере, либо при попадании компонента в область видимости на клиенте - то есть мы сознательно создаем ошибку гидрации, и гасим ее через пропс suppressHydrationWarning: true
.
Вся магия, почему разметка на клиенте не ломается, а переиспользуется, как раз заключается в хаке с dangerouslySetInnerHTML: { __html: '' }
.
У решения конечно же есть минусы:
- приходится создавать лишний тег - враппер
- по умолчанию код все-равно остается в клиентском бандле
const LazyRender = ({ children, cacheEnabled, cacheKey, serverCache }) => {
const containerRef = useRef(null);
const isVisible = useObserver(containerRef);
if (cacheEnabled && typeof window === 'undefined') {
let html: string;
if (serverCache.has(cacheKey)) {
html = serverCache.get(cacheKey);
} else {
const reactDomServer = require('react-dom/server');
html = reactDomServer.renderToString(children);
serverCache.set(cacheKey, html);
}
return React.createElement('div', {
dangerouslySetInnerHTML: { __html: html },
});
}
if (isVisible) {
return React.createElement('div', {}, children);
}
return React.createElement('div', {
ref: containerRef,
suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: '' },
});
};
Основной минус подхода - React Context приложения будет недоступен в children компоненте. Мы планируем использовать это для микрофронта (а футер как раз такой микрофронт), где это кажется не проблема, и даже хорошая практика.