لماذا Hooks؟
قبل React 16.8، كانت إدارة الحالة تتطلّب class components — كود طويل ومعقّد. الـ Hooks سمحت لنا باستخدام الحالة والدورة الحياتية في دوال بسيطة.
النتيجة: كود أقصر بـ30%، أسهل قراءةً، وأقلّ أخطاءً.
useState: إدارة الحالة
الاستخدام الأساسي
import { useState } from "react";
function Counter() {
// [القيمة الحالية, دالة التحديث] = useState(القيمة الأولية)
const [count, setCount] = useState(0);
return (
<div>
<p>العدد: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(0)}>إعادة</button>
</div>
);
}كل نقرة تستدعي setCount، فيُعاد رسم المكوّن تلقائياً بالقيمة الجديدة.
مع النصوص والكائنات
useState تعمل مع أيّ نوع بيانات:
const [name, setName] = useState("");
const [user, setUser] = useState({ name: "", age: 0 });
// تحديث كائن — انسخه ثم عدّله
setUser({ ...user, age: 30 });خطأ شائع: تعديل الكائن مباشرة بـ user.age = 30 — React لن يعيد الرسم لأنه لم يكتشف تغييراً في المرجع.
الشكل الدالي (Functional Update)
عندما يعتمد التحديث على القيمة السابقة، استخدم الشكل الدالي:
// ❌ قد يسبب مشاكل
setCount(count + 1);
// ✅ آمن دائماً
setCount((prev) => prev + 1);هذا مهم خصوصاً في async/await أو setTimeout.
useEffect: الآثار الجانبية
"الآثار الجانبية" = أي شيء يخرج خارج حسابات المكوّن: جلب بيانات، اشتراكات، تعديل DOM مباشر، المؤقّتات.
المثال الأساسي
import { useState, useEffect } from "react";
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// يُنفَّذ بعد كل تحديث لـ userId
fetch(`/api/users/${userId}`)
.then((r) => r.json())
.then(setUser);
}, [userId]); // ← مصفوفة التبعيات
if (!user) return <p>جارٍ التحميل...</p>;
return <h1>{user.name}</h1>;
}القاعدة: أيّ متغيّر تستخدمه داخل useEffect ويأتي من خارجه (props, state) يجب إضافته لمصفوفة التبعيات.
متى يعمل useEffect؟
| مصفوفة التبعيات | متى يعمل |
|------|-----------|
| [] (فارغة) | مرّة واحدة فقط بعد أول render |
| [x] | بعد كل تغيّر في x |
| غير موجودة | بعد كل render (نادراً ما نريد هذا) |
التنظيف (Cleanup)
إن بدأت شيئاً (اشتراك، timer)، نظّفه عند إلغاء المكوّن:
useEffect(() => {
const timer = setInterval(() => {
console.log("تكّ كل ثانية");
}, 1000);
// هذه الدالة تعمل عند إلغاء المكوّن أو قبل الـ effect التالي
return () => clearInterval(timer);
}, []);نسيان التنظيف = تسرّب ذاكرة (memory leak).
قواعد Hooks
تذكّر دائماً:
- استخدمها في أعلى المكوّن فقط — لا داخل if/else/for/while.
- من Function Components فقط — ليس من classes أو دوال عادية.
- الترتيب ثابت — React يعتمد على ترتيب الاستدعاء.
// ❌ خطأ — useState داخل شرط
function Bad({ active }) {
if (active) {
const [x, setX] = useState(0); // خطأ!
}
}
// ✅ صحيح
function Good({ active }) {
const [x, setX] = useState(0);
if (!active) return null;
return <div>{x}</div>;
}مثال متكامل: نموذج تسجيل
import { useState } from "react";
function SignupForm() {
const [form, setForm] = useState({ email: "", password: "" });
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
async function handleSubmit(e) {
e.preventDefault();
setLoading(true);
setError("");
try {
const res = await fetch("/api/signup", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(form),
});
if (!res.ok) throw new Error("فشل التسجيل");
alert("تم بنجاح!");
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={form.email}
onChange={(e) => setForm({ ...form, email: e.target.value })}
placeholder="البريد"
/>
<input
type="password"
value={form.password}
onChange={(e) => setForm({ ...form, password: e.target.value })}
placeholder="كلمة السر"
/>
{error && <p style={{ color: "red" }}>{error}</p>}
<button disabled={loading}>
{loading ? "جارٍ..." : "تسجيل"}
</button>
</form>
);
}أخطاء شائعة
- "Cannot update state on unmounted component": حدّث الحالة داخل useEffect مع تنظيف
- حلقة لا نهائية:
useEffectيُعدّل حالة بدون مصفوفة تبعيات — يُعاد الرسم فيعيد useEffect العمل - البيانات قديمة (stale closure): استخدم الشكل الدالي
setX(prev => ...)
الأسئلة الشائعة
هل أحفظ Hooks في ملف واحد؟
لا — كل مكوّن له hooks خاصّة. يمكن إنشاء custom hooks (مثل useAuth, useFetch) لمشاركة المنطق بين المكوّنات.
ما الفرق بين useState و useReducer؟
useState للقيم البسيطة. useReducer عند وجود تحديثات معقّدة مترابطة (سلّة تسوق مثلاً).
هل يعمل useState مع TypeScript؟
نعم، وبشكل ممتاز. النوع يُستنتج تلقائياً من القيمة الأولية، أو يمكنك تحديده: useState<User | null>(null).
مقالات ذات صلة
بناء REST API كامل بـ Node.js و Express: مشروع عملي
اصنع REST API جاهز للإنتاج بـ Node.js — CRUD كامل، معالجة أخطاء، التحقّق من البيانات، وربط PostgreSQL.
الفرق بين SSR و SSG و ISR: متى تستخدم كل واحد؟
ثلاث استراتيجيات للعرض في Next.js — أيّها الأنسب لموقعك؟ شرح عملي بأمثلة وحالات استخدام حقيقية.
حل مشكلة CORS في Express: الدليل العملي الكامل
واجهت خطأ CORS في تطبيق Node.js؟ تعلّم ما هو، لماذا يحدث، وكيف تُصلحه بطريقة صحيحة (ليس بفتح كل شيء).