React: useEffect ودورة حياة التطبيق

آخر تحديث: 2026-10-26

إذا كان useState هو «ذاكرة» المكون، فإن useEffect هو «الكاميرا» الخاصة به — فهو يتيح لك القيام ببعض الأمور الإضافية بعد عرض المكون على الشاشة («بعد التقاط الصورة»): طلب البيانات، والاشتراك في الأحداث، والتعديل على DOM، وضبط المؤقتات.


1. ما ستتعلمه



2. قصة صفحة تحميل البيانات

(1) المشكلة: طلبات البيانات تتسبب في حلقة لا نهائية

تريد أليس طلب بيانات المستخدم عند تحميل المكون، لذا كتبت ما يلي:

JSX
function UserProfile({ userId }) {
  const [user, setUser] = React.useState(null)

  // ❌ Error:Requesting data directly within a component's function body
  fetch('/api/users/' + userId)
    .then(res => res.json())
    .then(data => setUser(data))

  return <div>{user?.name}</div>
}
▶ جرّب الكود

السؤال:

  1. fetch → setUser(data) → إعادة عرض المكون
  2. إعادة العرض → تنفيذ fetch مرة أخرى → إعادة تعيين الحالة → إعادة العرض...
  3. حلقة لا نهائية! الصفحة متوقفة.

تحتاج أليس إلى طريقة لـ «التنفيذ مرة واحدة فقط عند تركيب المكون».

(2) حل useEffect

JSX
import { useState, useEffect } from 'react'

function UserProfile({ userId }) {
  const [user, setUser] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  // ✅ Correct:useEffect + Empty array of dependencies
  useEffect(() => {
    fetch('/api/users/' + userId)
      .then(res => {
        if (!res.ok) throw new Error('Failed to load')
        return res.json()
      })
      .then(data => {
        setUser(data)
        setLoading(false)
      })
      .catch(err => {
        setError(err.message)
        setLoading(false)
      })
  }, [userId])  // Only at userId Re-execute when changes occur

  if (loading) return <p>Loading......</p>
  if (error) return <p>Error:{error}</p>
  return <h1>{user.name}</h1>
}
▶ جرّب الكود

المزايا: useEffect يفصل الآثار الجانبية عن منطق العرض — حيث يقتصر دور العرض على واجهة المستخدم فقط، بينما يتم تنفيذ الآثار الجانبية بعد اكتمال عملية العرض.



3. أساسيات useEffect

(1) قواعد النحو الأساسية

JSX
useEffect(() => {
  // Side Effect Logic(Execute after rendering is complete)
  console.log('Component rendering complete!')
  
  return () => {
    // Cleanup Function(Run before unloading or re-executing a component)
    console.log('Addressing Side Effects')
  }
}, [Dependency Array])
▶ جرّب الكود
القسم الوصف
() => { ... } استدعاء رد الفعل الجانبي، الذي يتم تنفيذه بعد إتمام عرض المحتوى على الشاشة
return () => { ... } وظيفة التنظيف؛ تُنفَّذ قبل إعادة التنفيذ نتيجة لإلغاء تثبيت أحد المكونات أو تغييرات في التبعيات
[dependencies] مصفوفة تتحكم في توقيت إعادة تشغيل useEffect
100%
graph TB
    A[Component Rendering] --> B[React Submit DOM Update]
    B --> C[useEffect Callback Execution]
    C --> D{Changes in Dependencies?}
    D -->|is | E[Execute the previous cleanup function]
    E --> F[Re-execute useEffect]
    D -->|No| G[Skip]
    F --> H{Component Uninstallation?}
    G --> H
    H -->|is | I[Execute the cleanup function]
    
    style C fill:#61dafb,color:#000
    style I fill:#ff6b6b,color:#fff

(2) ثلاثة أنماط لمصفوفات التبعية

JSX
// Pattern 1:Empty array [] — Execute only once when mounted
useEffect(() => {
  console.log('Component Mounting')
  fetch('/api/init').then(setData)
}, [])  // Never re-execute

// Pattern 2:Dependencies [a, b] — Re-execute when dependencies change
useEffect(() => {
  console.log('userId It has changed.:', userId)
  fetch('/api/users/' + userId).then(setUser)
}, [userId])  // userId Re-execute when changes occur

// Pattern 3:No array dependencies — Executed on every render
useEffect(() => {
  console.log('Executed on every render')
})  // Note:No array!
▶ جرّب الكود
مجموعة التبعيات توقيت التنفيذ حالات الاستخدام
[] مرة واحدة فقط عند التثبيت تهيئة طلبات البيانات والاشتراكات
[dep1, dep2] عند التثبيت + عند تغير التبعيات تغير البيانات مع المعلمات
مجموعات لا حصر لها بعد كل عملية عرض نادرًا ما تُستخدم (عادةً ما تُستبدل بطرق أخرى)


4. وظائف التنظيف

تعد وظائف التنظيف أساسية لمنع تسربات الذاكرة وحالات التنافس.

(1) مسح المؤقت

▶ جرّب الكود

(2) تنظيف مستمعي الأحداث

▶ جرّب الكود

(3) طلب الإلغاء (لتجنب حالات التنافس)

JSX
function SearchResults({ query }) {
  const [results, setResults] = useState([])

  useEffect(() => {
    let cancelled = false

    fetch('/api/search?q=' + query)
      .then(res => res.json())
      .then(data => {
        // ✅ If the component has been uninstalled or query Has changed,Do not update status
        if (!cancelled) {
          setResults(data)
        }
      })

    // ✅ Cleanup:Uncheck
    return () => {
      cancelled = true
    }
  }, [query])

  return <div>{results.map(r => <p key={r.id}>{r.name}</p>)}</div>
}
// When users quickly switch search terms:
// 1. Search"A" → useEffect Execute
// 2. Search"AB" → Clear Function Markers cancelled=true → useEffect Re-execute
// 3. Search"A" the response came back → cancelled=true → Do not update status(Avoid flashing) 
▶ جرّب الكود

الناتج:

TEXT
العنوان الفرعي: "useEffect Life Cycle Demonstration". الحالة: count، logs. يتولى useEffect إدارة الآثار الجانبية. تحديث عبر مؤقت

▶ مثال: شرح شامل لوظائف التنظيف

▶ جرّب الكود

الناتج:

TEXT
العداد يزيد +1 تلقائيًا كل ثانيتين، ومفتاح المسافة +5. السجل: "⏱️ Timer started". التفكيك → "⏱️ Timer cleared"، "⌨️ Keyboard listening removed"


5. ربط useEffect بدورة حياة المكون

في عصر مكونات الفئات، كان هناك ثلاثة «هوكات» لدورة الحياة؛ أما في عصر «الهوكات»، useEffect—فإن «هوك» واحدًا يؤدي عمل ثلاثة:

100%
graph LR
    subgraph "The Lifecycle of Class Components"
        A1[componentDidMount] --> A2[componentDidUpdate]
        A2 --> A3[componentWillUnmount]
    end
    
    subgraph "Hooks 's  useEffect"
        B1["useEffect(fn, [])<br/>Execute when mounted"] 
        B2["useEffect(fn, [dep])<br/>Mount + run when deps change"]
        B3["return cleanup<br/>Clean up before uninstalling or rerunning"]
    end

الناتج:

TEXT
العنوان الفرعي: "useEffect Life Cycle Demonstration". يعرض: "useEffect Life Cycle Demonstration". الحالة: count (setter: setCount)، logs (setter: setLogs). يتولى useEffect إدارة الآثار الجانبية. تحديث قائم على مؤقت
دورة حياة مكونات الفئة useEffect المقابل
componentDidMount useEffect(() => { ... }, [])
componentDidUpdate useEffect(() => { ... }) (مجموعات لا حصر لها) أو useEffect(() => { ... }, [dep])
وظائف التنظيف في componentWillUnmount useEffect(() => { return () => { ... } }, [])

▶ مثال: استخدام useEffect واحد للتعامل مع مراحل متعددة من دورة الحياة

JSX
// ============================================
// Example:Chat Room Component——One useEffect Covering the Entire Lifecycle
// ============================================

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([])

  useEffect(() => {
    // === componentDidMount + componentDidUpdate ===
    console.log(`🟢 Connect to the chat room:${roomId}`)
    const connection = createConnection(roomId)
    connection.connect()

    connection.on('message', (msg) => {
      setMessages(prev => [...prev, msg])
    })

    // === componentWillUnmount + Clean Up Before Re-Execution ===
    return () => {
      console.log(`🔴 Leave the chat room:${roomId}`)
      connection.disconnect()
    }
  }, [roomId])  // roomId When things change:Disconnect the old connection → Join a New Chat Room

  return (
    <div>
      <h3>Chat Room:{roomId}</h3>
      {messages.map((msg, i) => <p key={i}>{msg}</p>}
    </div>
  )
}

// Usage
<ChatRoom roomId="general" />     // Connect general Chat Room
<ChatRoom roomId="random" />      // roomId Changes → Disconnect general → Connect random
// When a component is unloaded → Disconnect the current connection(Execution of the cleanup function)
▶ جرّب الكود

الناتج:

TEXT
عند التركيب: console "🟢 Connected to chat room: general". تبديل roomId: "🔴 Left: general" → "🟢 Connected: random". عند التفكيك: قطع الاتصال.


6. useEffect مقابل useLayoutEffect

يوفر React خطافين من نوع «الآثار الجانبية»، يختلفان من حيث توقيت التنفيذ.

100%
graph LR
    A[Trigger Rendering] --> B[Computational Virtual DOM]
    B --> C[DOM Update]
    C --> D{useLayoutEffect?}
    D -->|is | E[Execute in parallel<br/>Blocking Browser Rendering]
    D -->|No| F[Browser rendering complete]
    F --> G[useEffect Asynchronous Execution]
    
    style E fill:#faad14,color:#000
    style G fill:#52c41a,color:#fff
الخطاف توقيت التنفيذ يعيق عملية العرض حالات الاستخدام
useEffect بعد عرض المتصفح (بشكل غير متزامن) ❌ 90% من الحالات: طلبات البيانات، مستمعو الأحداث، التسجيل
useLayoutEffect بعد تحديثات DOM، قبل عرض المتصفح (متزامن) ✅ قياس أبعاد DOM، وقراءة التخطيط، ومزامنة الرسوم المتحركة
▶ جرّب الكود

القواعد: ابدأ بكتابة useEffect؛ ولا تنتقل إلى useLayoutEffect إلا إذا واجهت مشكلات تتعلق بـ«الوميض» أو «تذبذب التخطيط».



7. مثال كامل: لوحة بيانات

▶ عرض المثال القابل للتشغيل (98 سطرًا)
▶ جرّب الكود

الناتج:

TEXT
🟢 متصل بغرفة الدردشة: ...
🔴 غادر غرفة الدردشة: ...

الناتج المتوقع: لوحة بيانات تحتوي على 4 بطاقات إحصائية، ومفتاح لتحديث البيانات في الوقت الفعلي، ومؤشر لحجم النافذة، ووقت آخر تحديث.


▶ المثال 3: استخدام useEffect لتنفيذ مزامنة البيانات في الوقت الفعلي

JSX
function LiveSearch({ apiBase }) {
  const [query, setQuery] = useState('')
  const [results, setResults] = useState([])
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    if (!query.trim()) { setResults([]); return }
    setLoading(true)
    const controller = new AbortController()
    fetch(`${apiBase}/search?q=${encodeURIComponent(query)}`, { signal: controller.signal })
      .then(res => res.json())
      .then(data => { setResults(data); setLoading(false) })
      .catch(err => { if (err.name !== 'AbortError') setLoading(false) })
    return () => controller.abort()
  }, [query, apiBase])

  return (
    <div style={{ maxWidth: 400, margin: '0 auto' }}>
      <input value={query} onChange={e => setQuery(e.target.value)}
        placeholder="Search users..." style={{ width: '100%', padding: 8, borderRadius: 4, marginBottom: 8 }} />
      {loading && <p>Searching...</p>}
      <ul style={{ listStyle: 'none', padding: 0 }}>
        {results.map(r => <li key={r.id} style={{ padding: 4, borderBottom: '1px solid #f0f0f0' }}>{r.name}</li>)}
      </ul>
    </div>
  )
}
▶ جرّب الكود

الناتج:

TEXT
يتولى useEffect إدارة الآثار الجانبية

الناتج:

TEXT
يعرض: "Searching...". الحالة: query (setter: setQuery)، results (setter: setResults)، loading (setter: setLoading). الإدخال: Search users.... القائمة: {r.name}. يتولى useEffect إدارة الآثار الجانبية. جلب بيانات غير متزامن/حالة التحميل

▶ المثال 4: استخدام useEffect لمراقبة التغييرات في حجم النافذة

▶ جرّب الكود

الناتج:

TEXT
يتولى useEffect إدارة الآثار الجانبية

الناتج:

TEXT
يعرض: "📱 Single column layout". الحالة: size (setter: setSize). يتولى useEffect إدارة الآثار الجانبية

▶ المثال 5: استخدام useEffect + localStorage للحفاظ على الحالة

▶ جرّب الكود

الناتج:

TEXT
تُحفظ قائمة المهام في localStorage. أضف مهمة، وانقر للتبديل (يتوسطها خط). تحديث الصفحة → تبقى المهام. الإضافة بمفتاح Enter.


❓ أسئلة شائعة

س ما الفرق بين useEffect وكتابة الكود مباشرةً داخل المكون؟
ج لا يتم تشغيل الكود الموجود داخل useEffect إلا بعد اكتمال عملية العرض، وبالتالي فهو لا يعطل واجهة المستخدم. أما الكود المكتوب مباشرةً داخل جسم دالة المكون فيتم تشغيله أثناء عملية العرض (مما يؤدي إلى تعطيلها) ويتم تنفيذه في كل عملية عرض. والفرق الرئيسي هو أن useEffect يسمح لك بإجراء عملية التنظيف عند إلغاء تثبيت المكون.
س لماذا يتم تشغيل وظيفة التنظيف في useEffect قبل كل عملية إعادة تنفيذ؟
ج هذا ضمان تصميمي في React — فهو يمنع «حالات التنافس» الناتجة عن التغييرات في التبعيات. على سبيل المثال، عندما يتغير userId، يتم تنظيف الطلب القديم قبل بدء طلب جديد، مما يضمن أن البيانات الجديدة تحل محل البيانات القديمة. إذا كان الطلب الجديد سيحل محل القديم مباشرةً، فقد يحدث خطأً يتمثل في «إكمال الطلب القديم أولاً → عودة البيانات إلى حالتها السابقة».
س ما هو ترتيب تنفيذ عدة خطافات useEffect؟
ج يتم تنفيذها بالترتيب الذي تظهر به في الكود. لذلك، يجب وضع خطافات useEffect التي تتولى «التهيئة» (مثل تحميل البيانات) أولاً، وتلك التي تتولى «الاستجابة للتغييرات» (مثل مزامنة الحالة مع localStorage) أخيرًا. أثناء عملية عرض واحدة، يتم تنفيذ جميع خطافات useEffect بالتسلسل.
س ما الفرق بين useEffect وuseLayoutEffect؟
ج يختلفان في توقيت تنفيذهما: يتم تنفيذ useEffect بشكل غير متزامن بعد تحديث DOM (بعد قيام المتصفح بعرض الصفحة)، وبالتالي لن يلاحظ المستخدمون أي وميض؛ أما useLayoutEffect فيُنفَّذ بشكل متزامن بعد تحديث DOM (قبل أن يقوم المتصفح بعرضه)، مما يتيح لك تعديل DOM ثم جعل المتصفح يعرضه. استخدم useEffect في 99% من الحالات. استخدم useLayoutEffect فقط عندما «تحتاج إلى إجراء حسابات بناءً على تخطيط DOM ثم تعديل DOM بشكل متزامن» (مثل تعديل موضع عنصر ما بعد قياس أبعاده).

📖 ملخص


📝 تمارين

  1. المشكلة الأساسية (صعوبة ⭐): أنشئ مكونًا باسم PageTitle. استخدم useEffect لتغيير عنوان الصفحة إلى "مرحبًا بكم في موقعي الإلكتروني" عند تثبيت المكون، وإعادة العنوان إلى ما كان عليه عند إلغاء تثبيته.
  2. تمرين متقدم (مستوى الصعوبة ⭐⭐): قم بإنشاء مكون OnlineStatus يستخدم useEffect للاستماع إلى الأحداث الواردة من navigator.onLine وonline/offline، وعرض حالة الشبكة في الوقت الفعلي.
  3. التحدي (الصعوبة: ⭐⭐⭐): أنشئ هوكًا مخصصًا باسم usePolling يقبل المعلمات url وinterval، ويقوم باستقصاء الواجهة مرة واحدة كل interval مللي ثانية، ويعيد القيمة { data, isLoading, error }. يجب أن يدعم الهوك إيقاف الاستقصاء مؤقتًا واستئنافه.
Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%