تعذر تحديث حالة React على مكون غير مركب: كيفية الإصلاح وتسريع الكود

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

خلل مألوف يزعج كل مطور React

\n

هل سبق لك أن رأيت في وحدة تحكم المتصفح تحذيرًا Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application.؟ إذا كان الأمر كذلك - فأنت لست وحدك. هذا السؤال على Stack Overflow حصل على آلاف المشاهدات وأصبح صداعًا حقيقيًا للمبتدئين والمطورين ذوي الخبرة في React.

\n

في هذه المقالة، سنفهم لماذا يحدث هذا الخطأ، وكيفية إصلاحه باستخدام TypeScript، والأهم من ذلك - كيفية تحسين أداء تطبيقك بحيث لا تظهر هذه التحذيرات مرة أخرى. هيا بنا!

\n\n

لماذا يصرخ React بشأن المكون غير المركب؟

\n

تخيل: فتح المستخدم صفحة، وبدأ تحميل البيانات (على سبيل المثال، عبر fetch أو axios)، لكنه لم ينتظر الرد وانتقل إلى شاشة أخرى. المكون الذي بدأ الطلب قد تم إلغاء تركيبه بالفعل (أُزيل من DOM)، لكن العملية غير المتزامنة لا تزال قيد التنفيذ. عندما يصل الرد، يحاول الكود تحديث حالة (state) مكون غير موجود. يحتج React بحق: "مرحبًا، لا يمكنني تحديث ما هو غير موجود!".

\n

في TypeScript، تكون هذه المشكلة ملحوظة بشكل خاص إذا كنت تستخدم كتابة صارمة وتنسى وظائف التنظيف (cleanup) في useEffect.

\n\n

مثال كلاسيكي للمشكلة (TypeScript)

\n
import React, { useState, useEffect } from 'react';\n\ninterface User {\n  id: number;\n  name: string;\n}\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n  const [user, setUser] = useState<User | null>(null);\n\n  useEffect(() => {\n    fetch(`/api/users/${userId}`)\n      .then(res => res.json())\n      .then(data => setUser(data)); // خطأ! قد يكون المكون غير مركب\n  }, [userId]);\n\n  return <div>{user?.name}</div>;\n};
\n

إذا قام المستخدم بتبديل المسار بسرعة، سيتم حذف المكون UserProfile، وسيتم استدعاء setUser في غير محله. نحصل على تسرب للذاكرة وخطأ في وحدة التحكم.

\n\n

3 طرق لإصلاح الخطأ (مع أمثلة على TypeScript)

\n

دعنا نستعرض ثلاث طرق موثوقة تحل المشكلة وتحسن أداء تطبيق React.

\n\n

الطريقة 1: علامة باستخدام useRef (الأكثر شيوعًا)

\n

ننشئ علامة ref توضح ما إذا كان المكون مركبًا. نقوم بتحديث الحالة فقط إذا كانت العلامة true.

\n
import React, { useState, useEffect, useRef } from 'react';\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n  const [user, setUser] = useState<User | null>(null);\n  const isMounted = useRef(true);\n\n  useEffect(() => {\n    // إعادة تعيين العلامة عند كل تركيب\n    isMounted.current = true;\n\n    fetch(`/api/users/${userId}`)\n      .then(res => res.json())\n      .then(data => {\n        if (isMounted.current) {\n          setUser(data);\n        }\n      });\n\n    // التنظيف: تعيين العلامة إلى false عند إلغاء التركيب\n    return () => {\n      isMounted.current = false;\n    };\n  }, [userId]);\n\n  return <div>{user?.name}</div>;\n};
\n

الإيجابيات: البساطة، لا يتطلب مكتبات إضافية. السلبيات: يجب ألا تنسى التنظيف (cleanup).

\n\n

الطريقة 2: AbortController لإلغاء طلب fetch (موصى به)

\n

النهج الحديث والصحيح هو إلغاء الطلب نفسه، وليس مجرد تجاهل الرد. هذا يوفر موارد الخادم والعميل.

\n
import React, { useState, useEffect } from 'react';\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n  const [user, setUser] = useState<User | null>(null);\n  const [loading, setLoading] = useState(true);\n\n  useEffect(() => {\n    const controller = new AbortController();\n    const signal = con                

المدونات

توصيات الكتب