فهم الحالة في React
الحالة (State) هي واحدة من أقوى الميزات في React. تسمح للمكونات بإنشاء وإدارة بياناتها الخاصة، مما يجعل التطبيقات تفاعلية وديناميكية. عندما تتغير الحالة، يقوم React تلقائياً بإعادة عرض المكون لعكس البيانات الجديدة.
مفهوم رئيسي: الحالة خاصة ويتم التحكم فيها بالكامل بواسطة المكون. على عكس props التي يتم تمريرها من الوالد إلى الطفل، يتم إدارة الحالة داخل المكون نفسه.
خطاف useState
خطاف useState هو الطريقة الأساسية لإضافة حالة إلى المكونات الوظيفية. يُرجع مصفوفة بعنصرين: قيمة الحالة الحالية ودالة لتحديثها.
import React, { useState } from 'react';
function Counter() {
// تعريف متغير حالة باسم "count" بقيمة أولية 0
const [count, setCount] = useState(0);
return (
<div>
<p>لقد نقرت {count} مرة</p>
<button onClick={() => setCount(count + 1)}>
انقر هنا
</button>
</div>
);
}
اصطلاح التسمية: عند استخدام تفكيك المصفوفة مع useState، قم بتسمية متغير الحالة بشكل وصفي وأضف بادئة "set" لدالة التحديث. على سبيل المثال: [user, setUser]، [isLoading, setIsLoading].
كيف يعمل useState
لنقم بتحليل بناء جملة useState:
- الحالة الأولية: الوسيطة الممررة إلى
useState(0) هي قيمة الحالة الأولية
- الحالة الحالية: العنصر الأول من المصفوفة (
count) هو قيمة الحالة الحالية
- دالة تحديث الحالة: العنصر الثاني من المصفوفة (
setCount) هو دالة لتحديث الحالة
function Example() {
// متغيرات حالة متعددة في مكون واحد
const [age, setAge] = useState(25);
const [name, setName] = useState('John');
const [isActive, setIsActive] = useState(true);
return (
<div>
<p>الاسم: {name}، العمر: {age}</p>
<p>الحالة: {isActive ? 'نشط' : 'غير نشط'}</p>
<button onClick={() => setAge(age + 1)}>
زيادة العمر
</button>
<button onClick={() => setIsActive(!isActive)}>
تبديل الحالة
</button>
</div>
);
}
تحديث الحالة بالكائنات
عندما تكون الحالة عبارة عن كائن، يجب أن تكون حريصاً على الحفاظ على الخصائص الموجودة عند التحديث. React لا يدمج حالة الكائن تلقائياً - بل يستبدلها.
function UserProfile() {
const [user, setUser] = useState({
name: 'Alice',
age: 28,
email: 'alice@example.com'
});
const updateName = (newName) => {
// خطأ: سيؤدي هذا إلى فقدان age و email
// setUser({ name: newName });
// صحيح: نشر الخصائص الموجودة
setUser({
...user,
name: newName
});
};
const updateAge = () => {
setUser(prevUser => ({
...prevUser,
age: prevUser.age + 1
}));
};
return (
<div>
<h3>{user.name}</h3>
<p>العمر: {user.age}</p>
<p>البريد: {user.email}</p>
<button onClick={() => updateName('Bob')}>
تغيير الاسم
</button>
<button onClick={updateAge}>
زيادة العمر
</button>
</div>
);
}
خطأ شائع: لا تقم أبداً بتعديل الحالة مباشرة! استخدم دائماً دالة التحديث. كتابة count = count + 1 أو user.name = 'Bob' لن تؤدي إلى إعادة العرض وتكسر إدارة حالة React.
تحديث الحالة بالمصفوفات
على غرار الكائنات، عند تحديث حالة المصفوفة، قم بإنشاء مصفوفة جديدة بدلاً من تعديل المصفوفة الموجودة.
function TodoList() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const addTodo = () => {
if (input.trim()) {
// إضافة مهمة جديدة إلى النهاية
setTodos([...todos, { id: Date.now(), text: input }]);
setInput('');
}
};
const removeTodo = (id) => {
// تصفية المهمة ذات المعرف المطابق
setTodos(todos.filter(todo => todo.id !== id));
};
const clearAll = () => {
setTodos([]);
};
return (
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="أضف مهمة"
/>
<button onClick={addTodo}>إضافة</button>
<button onClick={clearAll}>مسح الكل</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<button onClick={() => removeTodo(todo.id)}>
حذف
</button>
</li>
))}
</ul>
</div>
);
}
التحديثات الوظيفية للحالة
عندما تعتمد الحالة الجديدة على الحالة السابقة، استخدم الشكل الوظيفي من setState. هذا يضمن أنك تعمل مع أحدث قيمة للحالة.
function Counter() {
const [count, setCount] = useState(0);
const incrementByThree = () => {
// خطأ: يزيد بمقدار 1 فقط
// setCount(count + 1);
// setCount(count + 1);
// setCount(count + 1);
// صحيح: كل تحديث يستخدم أحدث قيمة
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
};
return (
<div>
<p>العدد: {count}</p>
<button onClick={incrementByThree}>
زيادة بمقدار 3
</button>
</div>
);
}
أفضل ممارسة: استخدم الشكل الوظيفي setState(prev => ...) كلما اعتمدت حالتك الجديدة على الحالة السابقة. هذا يمنع الأخطاء المتعلقة بقيم الحالة القديمة.
رفع الحالة للأعلى
في بعض الأحيان، تحتاج مكونات متعددة إلى مشاركة نفس الحالة. في React، نتعامل مع ذلك بنقل الحالة إلى أقرب مكون أصلي مشترك وتمريرها عبر props.
function TemperatureCalculator() {
const [temperature, setTemperature] = useState('');
return (
<div>
<h2>محول درجات الحرارة</h2>
<TemperatureInput
scale="c"
temperature={temperature}
onTemperatureChange={setTemperature}
/>
<TemperatureInput
scale="f"
temperature={temperature}
onTemperatureChange={setTemperature}
/>
<BoilingVerdict celsius={parseFloat(temperature)} />
</div>
);
}
function TemperatureInput({ scale, temperature, onTemperatureChange }) {
const scaleNames = { c: 'مئوية', f: 'فهرنهايت' };
return (
<fieldset>
<legend>أدخل درجة الحرارة بـ {scaleNames[scale]}:</legend>
<input
value={temperature}
onChange={(e) => onTemperatureChange(e.target.value)}
/>
</fieldset>
);
}
function BoilingVerdict({ celsius }) {
if (isNaN(celsius)) return null;
return (
<p>
{celsius >= 100
? 'سيغلي الماء.'
: 'لن يغلي الماء.'}
</p>
);
}
رفع الحالة للأعلى: عندما يحتاج مكونان أو أكثر إلى مشاركة الحالة، انقل الحالة إلى أصلهم المشترك ومررها كـ props مع دوال لتحديثها. هذا يسمى "رفع الحالة للأعلى".
تمرين 1: عداد مع عمليات متعددة
أنشئ مكون AdvancedCounter بالميزات التالية:
- عرض العدد الحالي (بدءاً من 0)
- زر الزيادة (+1)
- زر النقصان (-1)
- زر الزيادة بمقدار 5 (+5)
- زر إعادة التعيين إلى 0
- عرض إجمالي عدد النقرات على الأزرار بشكل منفصل
تلميح: ستحتاج إلى متغيري حالة: واحد للعدد وواحد لإجمالي النقرات.
تمرين 2: سلة التسوق
أنشئ مكون سلة تسوق بسيط مع إدارة الحالة:
- مصفوفة من المنتجات (id، name، price، quantity)
- عرض كل منتج مع تفاصيله
- زر زيادة الكمية لكل منتج
- زر تقليل الكمية (الحد الأدنى 0)
- زر إزالة المنتج
- عرض السعر الإجمالي في الأسفل
إضافي: أضف إدخالاً لإضافة منتجات جديدة إلى السلة.
تمرين 3: نموذج بحقول متعددة
أنشئ نموذج تسجيل مستخدم مع حالة لحقول متعددة:
- اسم المستخدم (إدخال نصي)
- البريد الإلكتروني (إدخال بريد إلكتروني)
- العمر (إدخال رقمي)
- الاشتراك في النشرة الإخبارية (مربع اختيار)
- عرض جميع القيم في الوقت الفعلي أسفل النموذج
- زر الإرسال الذي يسجل كائن المستخدم الكامل
- زر المسح الذي يعيد تعيين جميع الحقول
تلميح: استخدم كائن حالة واحد بخصائص متعددة.