Next.js ile çalışırken karşılaşabileceğiniz en yaygın hatalardan biri, hydration failed hatasıdır. Bu yazımda, hatanın neden oluştuğunu ve çözüm yollarını anlatacağım.
Sebep #
Bu hataya React'in apilerinden biri olan hydrateRoot
adlı api neden olmaktadır. Bu api, React'i (çoğu durumda) interaktiflik eklemek için genellikle sunucu (react-dom/server) tarafından oluşturulan önceden oluşturulmuş HTML'ye eklemek için kullanılır. Next.js ile derlenmiş bir react uygulamasında bunu görmenizin nedeni budur. Next.js komponentleri sunucu tarafında (SSR - server-side-rendering) oluşturduğundan, arka planda hydrateRoot api'sini kullanır. Bir kullanıcı sitenizi ziyaret ettiğinde Next.js veya Gatsby veya Astro gibi diğer frameworkler (server side rendering sağlayan frameworkler) kullanıcıya statik olarak oluşturulmuş bir html sayfası sunar. Ardından, hydrateRoot
api tüm efektleri, state ve/veya event listenerları bu sayfaya ekler. React ui'i hydrate ederken uida render edilen içeriğin sunucu tarafından render edilen içerikle aynı olmasını bekler.
Hydration Failed Hatasına Neden Olan Olası Senaryolar #
Bu fikir akılda tutularak, bu hatanın gerçek hayat senaryolarında ortaya çıkmasının nedenlerinden bazıları aşağıda verilmiştir.
- Html etiketlerinizi yanlış nest ettiyseniz. Örneğin;
<span></span>
etiketinin içine bir<a></a>
etiketi yerleştirdiyseniz bu hataya sebep olacaktır. DOM api bu tagleri manuel olarak oluşturmanıza izin verdiği için client tarafında, html etiketlerinin hatalı şekilde iç içe yerleştirilmesiyle çalışabilirken, html etiketlerini yanlış nest ederek sunucu tarafında oluşturulan bir html sayfasın render etmek imkansızdır. DOM api hatalı htmli parse edemez. Burada, Material Ui ve Ant Desgin gibi bazı yaygın komponent kütüphanelerinin uygunsuz iç içe yerleştirilmiş html bileşenlerine sahip olduğunu, dolayısıyla bu hatayı almanızın nedeni bu bileşenler olabileceği sonucuna varmak doğru bir çıkarım olacaktır. - Eğer render senaryonuzda localStorage, sessionStorage veya window gibi tarayıcı tabanlı apiler kullanmaya çalışırsanız. SSR HTML'nizin ilk render sırasında tarayıcı apilerine erişimi olmayacağı için soruna neden olabilir.
- Yukarıdakileri kaputun altında yapmaya çalışan bir kütüphane.
- Render aşamasında client side senaryosu. Son projemde, render senaryomda client side ile renderı update olan
ThemeSwitcher
komponentimdi.
Hidrasyon Uyarısı Sorununa Neden Olan ThemeSwitcher
1'use client';2import { useTheme } from 'next-themes';3import { MoonIcon, SunIcon } from '@/components/icons/Icons.js';4import { twMerge } from 'tailwind-merge';56const ThemeSwitcher = () => {7 const { theme, setTheme } = useTheme();8 return (9 <button10 onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}11 className={twMerge(12 'w-6 h-6 ease flex items-center justify-center rounded-full p-1',13 theme === 'light' ? 'bg-dark-bg text-dark-text' : 'bg-light-bg text-light-text'14 )}15 aria-label='theme-switcher'16 >17 {theme === 'light' ? (18 <MoonIcon className={'fill-dark-bg'} />19 ) : (20 <SunIcon className={'fill-dark-bg'} />21 )}22 </button>23 );24};2526export default ThemeSwitcher;27
Bu bileşenin "use client"
özelliğine sahip olduğunu, dolayısıyla istemci tarafında işlendiğini söylediğinizi duyar gibiyim. Haklısınız. Ancak, komponent istemci tarafında oluşturulmuş olsa da Next.js yine de bu komponenti sunucu tarafında hydrate eder.
Nasıl Çözülür #
1. Temel olarak hatayı bastırabilirsiniz.
Hatayı Bastırma
1export default function App() {2 return3 (4 <div suppressHydrationWarning={true}>{new Date().toISOString()}</div>5 );6}
Eğer işin kolayına kaçmak ve bu hatayı bastırmak istiyorsanız bu sorununuzu çözer.
2. useEffect
Hookunu Kullanarak
Komponenti sunucu tarafı oluşturma ve ilk client tarafında oluşturma aşamalarında aynı içeriği oluşturduğundan emin olun. Bunu yaparak hydrate uyuşmazlığını önleyebilirsiniz. Bunu yapmak için ilk renderda loader render eden bir HydrateWrapper
komponenti oluşturdum, böylece ui güncellemelerinin neden olduğu hidrasyon hatasını önlemek için istemci ve sunucu eşleşti.
HydrateWrapper
1'use client';2import React from 'react';34const HydrateWrapper = ({5 children,6 loader,7}: {8 children: React.ReactNode;9 loader: React.ReactNode;10}) => {11 const [hydrated, setHydrated] = React.useState(false);1213 React.useEffect(() => {14 setHydrated(true);15 }, []);1617 if (!hydrated) return <>{loader}</>;18 return <div>{children}</div>;19};2021export default HydrateWrapper;22
Ardından hydrate hatası verecek komponentinizi bu komponente sarın.
Komponenti HydrateWrapper ile Sarma
1export default async function Page() {2 return (3 <HydrateWrapper loader={<ThemeSkeleton />}>4 <ThemeSwitcher />5 </HydrateWrapper>6 );7}
3. Belirli komponentlerde SSR'i devre dışı bırakabilirsiniz.
SSR'i Devre dışı Bırakma
1import dynamic from 'next/dynamic'23const ThemeSwitcher = dynamic(() => import('../components/theme-swithcer'), { ssr: false })45export default async function Page() {6 return (7 <div>8 <ThemeSwitcher />9 </div>10 )11}
Özet #
Next.js, Gatsby ve Astro gibi diğer react framework'leri, hydrateRoot
kullanarak sunucu tarafında işlenen statik sayfaları hydrate etmeye çalışırken, hydration failed uyarısı verebilir. İlk kullanıcı arayüzü sunucuda render edilenle eşleşmediğinde, başka bir deyişle sunucu tarafında render edilen html ile initial react render farklı olduğunda hata verir. Bunu kolayca bastırabilir, useEffect ile üstesinden gelebilir veya SSR'i devre dışı bırakabilirsiniz. Herhangi bir fikriniz varsa, bu sorunla karşılaştıysanız veya makalem hakkında geri bildiriminiz varsa lütfen aşağıya bir yorum bırakın. Okuduğunuz için teşekkürler.