تخطَّ إلى المحتوى

٩ يوليو ٢٠٢٦

حل خطأ Hydration failed في React و Next.js بالعربي

شارك المقال:

من أكثر الأخطاء إرباكًا للقادمين إلى Next.js وReact:

Error: Hydration failed because the initial UI does not match
what was rendered on the server.

أو شقيقه:

Warning: Text content did not match. Server: "09:41" Client: "09:42"

الصفحة تعمل غالبًا، لكن الرسالة الحمراء تملأ الـ console. لنفهم السبب الجذري ونحلّه بشكل صحيح.

ماذا يحدث وقت الـ Hydration؟

في Next.js يُرسم مكوّنك مرتين: مرة على الخادم (لإنتاج HTML سريع الظهور)، ومرة في المتصفح ليتولى React توصيله بالتفاعل. القاعدة الصارمة: الرسمتان يجب أن تكونا متطابقتين حرفيًّا. أي فرق — حتى مسافة أو رقم ثانية — يفجّر الخطأ.

الأسباب الأربعة وحلولها

السببمثالالحل
قيمة تتغير بين البيئتينDate.now()،\u200F Math.random()انقلها إلى useEffect
وسوم متداخلة بشكل غير صحيحp داخل p، جدول بلا tbodyصحّح البنية
كود «متصفح فقط» في الرندرwindow،\u200F localStorageفحص التركيب أو ssr: false
إضافات المتصفح تعدّل الصفحةمترجم، مدير كلمات مرورجرّب بنافذة خاصة

1) القيم المتغيرة: ارسمها بعد التركيب

الخادم رسم الساعة 09:41، والمتصفح وصلها 09:42 — تعارض حتمي:

// ❌ قيمة مختلفة في كل رندر
function Clock() {
  return <span>{new Date().toLocaleTimeString("ar-EG")}</span>;
}

// ✅ الخادم والمتصفح يرسمان "—" المتطابقة، ثم يحدّثها المتصفح وحده
function Clock() {
  const [time, setTime] = useState<string | null>(null);
  useEffect(() => {
    setTime(new Date().toLocaleTimeString("ar-EG"));
  }, []);
  return <span>{time ?? "—"}</span>;
}

نفس المبدأ لأي Math.random() أو قيمة من localStorage.

2) تداخل الوسوم: أشهر فخ صامت

HTML لا يسمح بفقرة داخل فقرة، والمتصفح «يصلّح» البنية الخاطئة بصمت — فيصبح DOM المتصفح مختلفًا عن شجرة React:

// ❌ مكتبات الـ UI كثيرًا ما ترندر <Typography> كـ <p>
<p>
  <p>نص متداخل</p>
</p>

// ❌ روابط Next القديمة حول عناصر كتلية داخل فقرة
<p>اقرأ <div>هذا</div></p>

القاعدة: افحص العنصر المذكور في رسالة الخطأ (React الحديث يسمّيه) وتأكد أن تداخله صالح في HTML — العناصر الكتلية لا تعيش داخل p.

3) كود المتصفح قبل أوانه

// ❌ window غير موجودة على الخادم أصلًا
const width = window.innerWidth;

// ✅ خيار أ: بعد التركيب
useEffect(() => setWidth(window.innerWidth), []);

وإذا كان المكوّن كله بلا معنى على الخادم (خريطة، محرر، رسم بياني):

// ✅ خيار ب: استيراد ديناميكي بلا رندر خادمي
const Map = dynamic(() => import("./Map"), { ssr: false });

4) الاستثناء المشروع: suppressHydrationWarning

للنصوص التي يجب أن تختلف (طوابع زمنية مثلًا):

<time suppressHydrationWarning>{new Date().toLocaleString("ar-EG")}</time>

استخدمه بأضيق نطاق ممكن — على العنصر النصي نفسه، وليس على حاوية كاملة.

قائمة تحقّق سريعة

  • هل في الرندر Date أو random أو قراءة localStorage؟ → useEffect.
  • هل رسالة الخطأ تذكر وسمًا؟ افحص صحّة تداخله (خاصة p وa والجداول).
  • هل تعمل الصفحة في نافذة تصفح خاص؟ → المشكلة إضافة متصفح وليست كودك.
  • مكوّن متصفح بالكامل؟ → dynamic(..., { ssr: false }).

الخلاصة

خطأ الـ Hydration ليس غموضًا — هو دائمًا جملة واحدة: «الخادم رسم شيئًا والمتصفح رسم شيئًا آخر». حدّد مصدر الاختلاف (قيمة متغيرة، بنية وسوم، كود متصفح) وطبّق الحل المناسب من فوق، ولا تلجأ لإسكات التحذير إلا للحالة النصية المشروعة.

أكمل الأساس في مسار React بالعربي ومسار Next.js، وقارن بين المكتبتين في الفرق بين Next.js و React، وإذا طاردك خطأ undefined فدليلك في حل Cannot read properties of undefined.

📚 مصادر رسمية للتعمّق: التوثيق الرسمي لـ React

الأسئلة الشائعة

ما معنى الـ Hydration في React أصلًا؟

الخادم يرسل HTML جاهزًا ليظهر فورًا، ثم يأتي جافاسكريبت React في المتصفح ويوصّل هذا الـ HTML بالأحداث والحالة — هذه العملية اسمها Hydration. الخطأ يحدث عندما يكتشف React أن ما رسمه المتصفح لا يطابق ما أرسله الخادم.

هل suppressHydrationWarning حل آمن؟

هو مقبول لحالة واحدة: عنصر نصي صغير تعرف أنه سيختلف حتمًا بين الخادم والمتصفح (كالوقت الحالي). لا تستخدمه لإسكات خطأ لا تفهم سببه — الفرق الحقيقي في الشجرة يبقى موجودًا وقد يسبب سلوكًا غريبًا لاحقًا.

لماذا يختفي الخطأ عند إغلاق إضافات المتصفح؟

بعض الإضافات (مثل مترجمات الصفحات ومديري كلمات المرور) تعدّل HTML الصفحة قبل أن يكمل React الـ hydration، فيرى فرقًا لم يكتبه كودك أصلًا. جرّب دائمًا في نافذة تصفح خاص قبل أن تطارد الخطأ في كودك.

اقرأ أيضًا

تصفّح كل المقالات في المدوّنة، أو ابدأ التعلّم من المسارات و خرائط الطريق.