🧠 کنترل رابط کاربری در React: State در برابر Ref در برابر CSS (با یک مثال واقعی)

پیش از رفتن سراغ مثال‌ها، مهم است تفاوت state و ref را در React خوب بفهمیم.

🔵 State در برابر 🔴 Ref (درک سریع)

🔵 State داده‌ای است که رابط کاربری شما را تعریف می‌کند. تغییرش باعث رندر دوباره می‌شود، React کاملاً رابط کاربری را کنترل می‌کند و برای منطق اپلیکیشن استفاده می‌شود. 👉 اگر روی چیزی که کاربر بر اساس وضعیت اپ می‌بیند اثر دارد، باید state باشد.

🔴 Ref ارجاع مستقیم به یک عنصر DOM یا یک مقدار تغییرپذیر است. رندر دوباره ایجاد نمی‌کند، React تغییراتش را دنبال نمی‌کند و برای کارهای دستوری استفاده می‌شود. 👉 اگر فقط یک تعامل مستقیم با DOM یا یک افکت موقت است، ref کافی است.

🔵 ۱. State — رابط کاربری تحت کنترل React

state یعنی React رابط کاربری را بر اساس تغییر داده رندر می‌کند. کاربردها: لایک/آنلایک، باز و بسته شدن مودال، وضعیت حذف، مدیریت فرم.

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

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

👉 رابط کاربری کاملاً با state هدایت می‌شود.

🔴 ۲. Ref — کنترل مستقیم DOM

ref اجازه می‌دهد عناصر DOM را بدون رندر دوباره React تغییر دهید. کاربردها: فوکوس روی ورودی‌ها، اسکرول، انیمیشن‌های دستوری، افکت‌های موقت DOM.

const btnRef = useRef(null);

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

👉 React از این تغییر خبر ندارد.

🟢 ۳. CSS — بهترین برای افکت‌های بصری

CSS ساده‌ترین و کارآمدترین راه برای افکت‌های رابط کاربری است: افکت هاور، ترنزیشن، انیمیشن و بازخورد کلیک.

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

👉 هیچ جاوااسکریپتی لازم نیست.

⚖️ نکته کلیدی

همه چیز در رابط کاربری نباید state باشد. انتخاب روش اشتباه به رندرهای اضافی، منطق شلوغ و رفتار رابط کاربری سخت‌دیباگ می‌رسد.

🧩 مثال واقعی: چشمک دکمه حذف

در یک پروژه React به دکمه حذفی نیاز داشتم که پیش از حذف آیتم یک چشمک قرمز کوتاه نشان دهد.

❌ اشتباه: استفاده از state برای همه چیز

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

مشکل: یک رندر دوباره غیرضروری — برای یک افکت بصری ۲۰۰ میلی‌ثانیه‌ای زیادی است.

❌ ترکیبی: 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 باشد.