Когда я пытаюсь использовать https://usehooks-ts.com/react-hook/use-local-storage в Next.js следующим образом, я получаю
Необработанная ошибка времени выполнения: текстовое содержимое не соответствует HTML, отображаемый сервером.
Смотрите больше информации здесь: https://nextjs.org/docs/messages/react-hydration-error
const [toleranceH, setToleranceH] = useLocalStorage<number>('toleranceH', 3);
const [toleranceS, setToleranceS] = useLocalStorage<number>('toleranceS', 3);
const [toleranceL, setToleranceL] = useLocalStorage<number>('toleranceL', 3);
const [results, setResults] = useState<MegaColor[]>([]);
const debouncedToleranceH = useDebounce<number>(toleranceH, 200);
const debouncedToleranceS = useDebounce<number>(toleranceS, 200);
const debouncedToleranceL = useDebounce<number>(toleranceL, 200);
useEffect(() => {
const targetColorDetailsObject = getColorDetailsObject(targetColor);
const degreeTolerance = (360 / 100) * debouncedToleranceH;
const [hueMin, hueMax] = getHueTolerance(targetColorDetailsObject.hue(), degreeTolerance);
const filteredColors = getFilteredColors(targetColorDetailsObject, loadedMegaColors, hueMin, hueMax, debouncedToleranceS, debouncedToleranceL);
setResults(filteredColors);
return () => {
// console.info('cleanup');
};
}, [targetColor, loadedMegaColors, debouncedToleranceH, debouncedToleranceS, debouncedToleranceL]);
На этой странице справки я до сих пор не могу понять, что нужно настроить, чтобы я мог использовать как useLocalStorage
, так и useDebounce
.
Я нашел https://stackoverflow.com/a/73411103/470749, но не хочу принудительно устанавливать значение localStorage (оно должно устанавливаться только пользователем).
Я бы посоветовал проверить этот отличный пост о регидратации Джоша У. Комо.
Поскольку Next.js по умолчанию предварительно отображает каждую страницу, вам необходимо убедиться, что компонент, в котором вы вызываете window.localstorage
, отображается только на клиенте.
Простое решение:
hasMounted
const [hasMounted, setHasMounted] = useState(false);
useEffect
useEffect(() => {
// This will only be called once the component is mounted inside the browser
setHasMounted(true);
}, []);
if (!hasMounted) {
return null;
}
Чтобы сделать его более пригодным для повторного использования, вы можете использовать один из этих двух методов, которые по существу делают то же самое:
function ClientOnly({ children, ...delegated }) {
const [hasMounted, setHasMounted] = React.useState(false);
React.useEffect(() => {
setHasMounted(true);
}, []);
if (!hasMounted) {
return null;
}
/**
* Could also replace the <div></div> with
* <></> and remove ...delegated if no need
*/
return (
<div {...delegated}>
{children}
</div>
);
}
...
<ClientOnly>
<MyComponent /> // <--- client only stuff, safe to use useLocalStorage in here
</ClientOnly>
или
function useHasMounted() {
const [hasMounted, setHasMounted] = React.useState(false);
React.useEffect(() => {
setHasMounted(true);
}, []);
return hasMounted;
}
...
function ParentComponent() {
const hasMounted = useHasMounted();
if (!hasMounted) {
return null;
}
return (
<MyComponent />
);
}
...
function MyComponent() {
const [toleranceH, setToleranceH] = useLocalStorage<number>('toleranceH', 3);
const [toleranceS, setToleranceS] = useLocalStorage<number>('toleranceS', 3);
const [toleranceL, setToleranceL] = useLocalStorage<number>('toleranceL', 3);
...
}
...
Переусердствовав с этим или используя этот метод на верхнем уровне вашего дерева компонентов, вы убиваете возможности предварительного рендеринга Next.js и превращаете свое приложение в более «тяжелое» приложение на стороне клиента (см. влияние на производительность). Если вы используете window.localstorage
(вне компонентов, где у вас нет useEffect
), вы всегда должны обертывать:
if (typeof window !== 'undefined') {
// client-side code
}
Ах, да, извините, я отредактировал свой ответ, лучше всего обернуть его родительским компонентом с подходом useHasMounted
, чтобы никакое условие, которое возвращается в компоненте, не было выше некоторых других вызовов ловушек. Что касается проверки typeof widow
, я нашел ей применение только вне компонентов, в противном случае следующая может привести к ошибкам на сервере, например localStorage is not defined
.
Спасибо! Подход
<ClientOnly>
, похоже, сработал. ПодходuseHasMounted
нарушил это правило reactjs.org/docs/hooks-rules.html , как иif (typeof window === 'undefined') { return null; }
(как и предсказывала статья в Noble But Flawed Attempt joshwcomeau.com/react/the-perils-of-rehydration/ …).