🧠 التحكم بالواجهة في React: State مقابل Ref مقابل CSS (مع مثال حقيقي)

قبل الدخول في الأمثلة، من المهم فهم الفرق بين state وref في React بوضوح.

🔵 State مقابل 🔴 Ref (فهم سريع)

🔵 State بيانات تحدد واجهتك. تغييرها يسبب إعادة عرض، ويتحكم React بالواجهة كليًا، وتُستخدم لمنطق التطبيق. 👉 إذا كانت تؤثر في ما يجب أن يراه المستخدم حسب حالة التطبيق، فيجب أن تكون state.

🔴 Ref مرجع مباشر إلى عنصر DOM أو قيمة قابلة للتغيير. لا يسبب إعادة عرض، ولا يتتبع React تغييراته، ويُستخدم للأفعال الأمرية. 👉 إذا كان الأمر مجرد تعامل مباشر مع DOM أو تأثير مؤقت، فإن ref يكفي.

🔵 1. State — واجهة يتحكم بها React

الحالة تعني أن React يعرض الواجهة بناءً على تغيّر البيانات. الاستخدامات: الإعجاب وإلغاؤه، فتح النافذة المنبثقة وإغلاقها، حالة الحذف، التعامل مع النماذج.

const [isLiked, setIsLiked] = useState(false);

<button onClick={() => setIsLiked(!isLiked)}>
  {isLiked ? "❤️ Liked" : "🤍 Like"}
</button>

👉 الواجهة تقودها الحالة بالكامل.

🔴 2. Ref — تحكم مباشر بـ DOM

يتيح ref تعديل عناصر DOM دون إعادة عرض React. الاستخدامات: التركيز على الحقول، والتمرير، والحركات الأمرية، وتأثيرات DOM المؤقتة.

const btnRef = useRef(null);

btnRef.current.style.transform = "scale(0.9)";

👉 React لا يعلم بهذا التغيير.

🟢 3. CSS — الأفضل للتأثيرات البصرية

CSS أبسط وأكفأ طريقة لتأثيرات الواجهة: تأثيرات التمرير، والانتقالات، والحركات، والاستجابة للنقر.

.delete-btn:active {
  transform: scale(0.9);
  background-color: red;
}

👉 لا حاجة لأي JavaScript.

⚖️ الفكرة الأساسية

ليس كل شيء في الواجهة يجب أن يكون state. اختيار النهج الخاطئ يؤدي إلى إعادات عرض غير ضرورية ومنطق فوضوي وسلوك واجهة صعب التصحيح.

🧩 مثال حقيقي: وميض زر الحذف

في مشروع React احتجت إلى زر حذف يُظهر وميضًا أحمر سريعًا قبل حذف العنصر.

❌ خطأ: استخدام state لكل شيء

const [isFlashing, setIsFlashing] = useState(false);

المشكلة: إعادة عرض غير ضرورية — مبالغة لتأثير بصري مدته 200 ملي ثانية.

❌ مختلط: state + ref

// التأثير على الزر
btnRef.current.style.backgroundColor = "red";

// حذف المكوّن كاملًا
setIsDeleting(true);

المشكلة: مصدران للحقيقة وسلوك غير متسق للواجهة.

✅ صحيح: CSS + تبديل الصنف

const handleDeleteClick = (e) => {
  const btn = e.currentTarget;
  btn.classList.add("flash");
  setIsDeleting(true);

  setTimeout(() => {
    btn.classList.remove("flash");
    handleDelete(id);
  }, 200);
};
.delete-btn.flash {
  background-color: red;
  transform: scale(0.9);
}

🎯 الخلاصة

  • State ← لبيانات التطبيق الحقيقية
  • Ref ← للتعديل المباشر على DOM
  • CSS ← للتأثيرات البصرية البسيطة
💡 إذا كان الشيء لا يمثّل حالة التطبيق، فعلى الأرجح لا يجب أن يكون state.