State في React — دليل شامل useState 2026
دليل عملي مفصل لإدارة الحالة (State) في React — useState، التحديث، الحالات المعقدة، مع تمارين وحلول
في المقال السابق، تعلمت المكونات وكيفية تمرير Props. لكن Props ثابتة (لا تتغير). الآن سنتعلم State — وهي البيانات التي تتغير مع تفاعل المستخدم.
في هذا الدليل العملي، سنأخذك خطوة بخطوة لفهم useState وكيفية إدارة الحالة في React، مع تمارين وحلول.
ما هو State؟
State هي بيانات متغيرة داخل المكوّنة. عندما تتغير، React تُعيد رسم المكوّنة تلقائياً.
تشبيه بسيط: تخيل أن State هي ذاكرة المكوّنة. الـ Props هي "ما يُعطى لها"، والـ State هي "ما تتذكره".
الفرق بين Props و State
| المعيار | Props | State |
|---|---|---|
| المصدر | من الأب | داخلي |
| التغيير | ❌ لا يتغير | ✅ يتغير |
| الاستخدام | بيانات ثابتة | بيانات تفاعلية |
| المثال | اسم المستخدم | عدد النقرات |
useState Hook
useState هو Hook يسمح بإضافة State للمكونات الوظيفية.
الصيغة:
const [state, setState] = useState(initialValue);
شرح:
state: القيمة الحالية.setState: دالة لتحديث القيمة.initialValue: القيمة الأولية.
مثال بسيط:
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>العدد: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
شرح:
useState(0)→ يبدأ العدد بـ 0.- عند الضغط على الزر →
setCount(count + 1). - React تُحدّث
countوتُعيد رسم المكوّنة.
أنواع State
1. Number
const [count, setCount] = useState(0);
const [price, setPrice] = useState(99.99);
2. String
const [name, setName] = useState("");
const [email, setEmail] = useState("");
3. Boolean
const [isOpen, setIsOpen] = useState(false);
const [isDark, setIsDark] = useState(true);
4. Array
const [items, setItems] = useState<string[]>([]);
const [numbers, setNumbers] = useState<number[]>([1, 2, 3]);
5. Object
const [user, setUser] = useState({
name: "أحمد",
age: 25,
});
6. Union Types
type Status = "idle" | "loading" | "success" | "error";
const [status, setStatus] = useState<Status>("idle");
تحديث State
1. تحديث مباشر
setCount(10);
setName("محمد");
setIsOpen(true);
2. تحديث بناءً على القيمة السابقة
❌ خطأ:
setCount(count + 1);
setCount(count + 1); // لا يعمل! يضيف 1 فقط
✅ صحيح:
setCount((prev) => prev + 1);
setCount((prev) => prev + 1); // يضيف 2
السبب: React تجمّع التحديثات (batching) للأداء.
3. تحديث Array
❌ خطأ (لا يعمل):
items.push("جديد");
setItems(items);
✅ صحيح:
// إضافة
setItems([...items, "جديد"]);
// حذف
setItems(items.filter((item) => item !== "جديد"));
// تعديل
setItems(items.map((item) => (item === "قديم" ? "جديد" : item)));
4. تحديث Object
❌ خطأ:
user.name = "محمد";
setUser(user);
✅ صحيح:
setUser({ ...user, name: "محمد" });
مثال عملي: تطبيق عداد
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
const increment = () => setCount((prev) => prev + 1);
const decrement = () => setCount((prev) => prev - 1);
const reset = () => setCount(0);
return (
<div className="counter">
<h2>العدد: {count}</h2>
<div className="buttons">
<button onClick={decrement}>-</button>
<button onClick={reset}>إعادة</button>
<button onClick={increment}>+</button>
</div>
</div>
);
}
export default Counter;
مثال عملي: نموذج إدخال
import { useState } from "react";
function Form() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
alert(`الاسم: ${name}\nالبريد: ${email}`);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="الاسم"
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="البريد الإلكتروني"
/>
<button type="submit">إرسال</button>
</form>
);
}
مثال عملي: قائمة مهام
import { useState } from "react";
interface Todo {
id: number;
text: string;
completed: boolean;
}
function TodoApp() {
const [todos, setTodos] = useState<Todo[]>([]);
const [input, setInput] = useState("");
const addTodo = () => {
if (input.trim() === "") return;
setTodos([
...todos,
{ id: Date.now(), text: input, completed: false },
]);
setInput("");
};
const toggleTodo = (id: number) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
const deleteTodo = (id: number) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
return (
<div className="todo-app">
<h1>📝 قائمة المهام</h1>
<div className="input-group">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && addTodo()}
placeholder="أضف مهمة..."
/>
<button onClick={addTodo}>إضافة</button>
</div>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? "line-through" : "none" }}>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>🗑️</button>
</li>
))}
</ul>
</div>
);
}
State مع TypeScript
1. State بسيط
const [count, setCount] = useState<number>(0);
const [name, setName] = useState<string>("");
const [isOpen, setIsOpen] = useState<boolean>(false);
2. State مع Union
type Status = "idle" | "loading" | "success" | "error";
const [status, setStatus] = useState<Status>("idle");
3. State مع Array
interface Todo {
id: number;
text: string;
}
const [todos, setTodos] = useState<Todo[]>([]);
4. State مع Object
interface User {
name: string;
age: number;
}
const [user, setUser] = useState<User>({ name: "", age: 0 });
5. State مع Null
const [user, setUser] = useState<User | null>(null);
Lazy Initial State
إذا كانت القيمة الأولية تحتاج حساباً:
// ❌ بطيء (يُحسب في كل render):
const [data, setData] = useState(expensiveCalculation());
// ✅ سريع (يُحسب مرة واحدة):
const [data, setData] = useState(() => expensiveCalculation());
رفع الحالة (Lifting State Up)
عندما تحتاج مكونتان مشاركة State، ارفعه للمكوّن الأب.
مثال:
// المكوّن الأب
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<Child1 count={count} />
<Child2 count={count} setCount={setCount} />
</div>
);
}
function Child1({ count }: { count: number }) {
return <p>العدد: {count}</p>;
}
function Child2({ count, setCount }: { count: number; setCount: (n: number) => void }) {
return <button onClick={() => setCount(count + 1)}>+1</button>;
}
تمارين عملية
تمرين 1: عداد بسيط
أنشئ عداداً مع زر +1 و -1.
الحل:
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
);
}
تمرين 2: نموذج تسجيل
أنشئ نموذجاً بحقول: الاسم، البريد، كلمة المرور.
الحل:
function RegisterForm() {
const [form, setForm] = useState({ name: "", email: "", password: "" });
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setForm({ ...form, [e.target.name]: e.target.value });
};
return (
<form>
<input name="name" value={form.name} onChange={handleChange} />
<input name="email" value={form.email} onChange={handleChange} />
<input name="password" type="password" value={form.password} onChange={handleChange} />
</form>
);
}
تمرين 3: Toggle (زر تبديل)
أنشئ زراً يبدّل بين "مفعّل" و "معطّل".
الحل:
function Toggle() {
const [isOn, setIsOn] = useState(false);
return (
<button onClick={() => setIsOn(!isOn)}>
{isOn ? "✅ مفعّل" : "❌ معطّل"}
</button>
);
}
تمرين 4: قائمة تسوق
أنشئ قائمة تسوق مع إضافة وحذف.
الحل:
function ShoppingList() {
const [items, setItems] = useState<string[]>([]);
const [input, setInput] = useState("");
const add = () => {
if (input) {
setItems([...items, input]);
setInput("");
}
};
return (
<div>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button onClick={add}>إضافة</button>
<ul>
{items.map((item, i) => (
<li key={i}>
{item}
<button onClick={() => setItems(items.filter((_, j) => j !== i))}>❌</button>
</li>
))}
</ul>
</div>
);
}
تمرين 5: تبديل اللون
أنشئ زراً يبدّل لون خلفية بين الأبيض والأسود.
الحل:
function ColorToggle() {
const [isDark, setIsDark] = useState(false);
return (
<div style={{ background: isDark ? "#000" : "#fff", color: isDark ? "#fff" : "#000", padding: 40 }}>
<button onClick={() => setIsDark(!isDark)}>
{isDark ? "☀️ فاتح" : "🌙 داكن"}
</button>
</div>
);
}
تمرين 6: عداد مع حد أقصى
أنشئ عداداً لا يزيد عن 10.
الحل:
function LimitedCounter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(Math.min(10, count + 1))}>+</button>
<button onClick={() => setCount(Math.max(0, count - 1))}>-</button>
</div>
);
}
تمرين 7: نموذج معاينة مباشرة
أنشئ نموذجاً يعرض النص أثناء الكتابة.
الحل:
function LivePreview() {
const [text, setText] = useState("");
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<p>المعاينة: {text}</p>
</div>
);
}
تمرين 8: قائمة مهام كاملة
أنشئ قائمة مهام مع إضافة، حذف، وتعليم كمكتمل.
الحل:
interface Todo {
id: number;
text: string;
completed: boolean;
}
function TodoList() {
const [todos, setTodos] = useState<Todo[]>([]);
const [input, setInput] = useState("");
const add = () => {
if (input.trim()) {
setTodos([...todos, { id: Date.now(), text: input, completed: false }]);
setInput("");
}
};
return (
<div>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button onClick={add}>إضافة</button>
<ul>
{todos.map((t) => (
<li key={t.id}>
<input
type="checkbox"
checked={t.completed}
onChange={() =>
setTodos(todos.map((x) => (x.id === t.id ? { ...x, completed: !x.completed } : x)))
}
/>
<span style={{ textDecoration: t.completed ? "line-through" : "none" }}>{t.text}</span>
<button onClick={() => setTodos(todos.filter((x) => x.id !== t.id))}>🗑️</button>
</li>
))}
</ul>
</div>
);
}
حل المشاكل الشائعة
🔴 المشكلة 1: State لا يتحدّث
السبب: تحديث مباشر بدلاً من دالة التحديث.
الحل:
setCount((prev) => prev + 1);
🔴 المشكلة 2: Array لا يتحدّث
السبب: استخدام push بدلاً من spread.
الحل:
setItems([...items, newItem]);
🔴 المشكلة 3: Object لا يتحدّث
السبب: تعديل مباشر.
الحل:
setUser({ ...user, name: "محمد" });
🔴 المشكلة 4: حلقة لا نهائية
السبب: استدعاء setState داخل render.
الحل: استخدم useEffect (سنتعلمه لاحقاً).
جدول أوامر useState
| الأمر | الوظيفة |
|---|---|
useState(initial) | تعريف State |
setState(value) | تعيين قيمة |
setState(prev => ...) | تحديث بناءً على السابق |
useState(() => ...) | Lazy initial state |
قائمة تحقق نهائية
| المهمة | الحالة |
|---|---|
| فهم State و useState | ⬜ |
| الفرق بين Props و State | ⬜ |
| تحديث State بشكل صحيح | ⬜ |
| State مع Array و Object | ⬜ |
| State مع TypeScript | ⬜ |
| بناء قائمة مهام | ⬜ |
| حل التمارين الثمانية | ⬜ |
ماذا بعد هذا المقال؟
الآن بعد أن أتقنت State، أنت جاهز للمقال التالي:
- الأحداث (Events) — التعامل مع تفاعل المستخدم.
- النماذج (Forms) — إدارة المدخلات.
- useEffect — التأثيرات الجانبية.
الخلاصة
في هذا المقال، تعلمت:
- ✅ ما هو State ولماذا نحتاجه.
- ✅ الفرق بين Props و State.
- ✅
useStateHook وكيفية استخدامه. - ✅ تحديث State بشكل صحيح.
- ✅ State مع Array و Object.
- ✅ رفع الحالة (Lifting State Up).
- ✅ حل 8 تمارين عملية.
تذكر: State هو ما يجعل تطبيقات React تفاعلية. أتقنه جيداً.
هل أتقنت State؟ شاركنا في التعليقات!
المكونات في React — دليل شامل Components 2026
الأحداث والنماذج في React — دليل شامل 2026
📚 مقالات ذات صلة
مشروع React متكامل — تطبيق متجر إلكتروني 2026
مشروع عملي شامل لبناء تطبيق متجر إلكتروني بـ React و TypeScript — مع السلة، البحث، التصفية، والحفظ التلقائي
Context API في React — إدارة الحالة العامة 2026
دليل عملي مفصل لـ Context API في React — إنشاء Context، Provider، useContext، مع تمارين وحلول
React Router — التنقل بين الصفحات في React 2026
دليل عملي مفصل لـ React Router — التنقل، المسارات، الروابط، والمعاملات، مع تمارين وحلول