useState: چیست، هوک state و نحو در React

نویسنده: IT Sectr منتشر شده: 2026-07-04 زمان مطالعه: 9 دقیقه

useState — یک هوک داخلی React است که به کامپوننت‌های تابعی اجازه می‌دهد بدون تبدیل به کامپوننت‌های کلاسی، state محلی را مدیریت کنند. React 16.8 هوک‌ها را معرفی کرد و useState پرکاربردترین آنها شد و جایگزین this.state و this.setState از رویکرد کلاسی گردید. طبق React Documentation (2025)، بیش از 80% کامپوننت‌ها در برنامه‌های مدرن React از useState برای مدیریت داده‌های فرم، پرچم‌های رابط و شمارنده‌ها استفاده می‌کنند. هوک یک tuple شامل مقدار فعلی و تابع setter که state را به‌روزرسانی کرده و رندر مجدد کامپوننت را آغاز می‌کند، برمی‌گرداند.

نکات اصلی

  • useState — هوکی برای مدیریت state محلی در کامپوننت‌های تابعی React.
  • Tuple — یک جفت [مقدار, setter] برمی‌گرداند که setter تابع به‌روزرسانی state است.
  • رندر مجدد — فراخوانی setter همیشه کامپوننت را با مقدار جدید دوباره رندر می‌کند.
  • انواع داده — useState با اعداد، رشته‌ها، آرایه‌ها، اشیاء و مقادیر بولین کار می‌کند.
  • تغییرناپذیری — state را نمی‌توان مستقیماً تغییر داد، فقط از طریق تابع setter.

useState در React چیست

useState — هوک بنیادین React است که در نسخه 16.8 اضافه شده و به کامپوننت‌های تابعی امکان ذخیره و تغییر state محلی را می‌دهد. قبل از ظهور هوک‌ها، state فقط در کامپوننت‌های کلاسی از طریق this.state و this.setState قابل استفاده بود که کامپوننت‌های تابعی را صرفاً نمایشی می‌کرد. useState این محدودیت را برطرف کرد و امکان نوشتن کل برنامه را بر روی کامپوننت‌های تابعی فراهم نمود.

هوک یک آرگومان می‌گیرد — مقدار اولیه state — و یک آرایه دو عنصری برمی‌گرداند. عنصر اول مقدار فعلی state است و عنصر دوم تابعی برای به‌روزرسانی آن. React تضمین می‌کند که تابع setter پایدار است و بین رندرها تغییر نمی‌کند که این امکان انتقال امن آن به کامپوننت‌های فرزند و استفاده در closureها را فراهم می‌کند.

طبق React DevTools Usage Survey (2024)، useState در 96% برنامه‌های React استفاده می‌شود که آن را به پرکاربردترین هوک در اکوسیستم تبدیل می‌کند. حتی در برنامه‌هایی که از state سراسری از طریق Redux یا Zustand استفاده می‌کنند، state محلی از طریق useState مکانیزم اصلی مدیریت state UI باقی می‌ماند — مودال‌های باز، مقادیر فیلدهای ورودی، تب‌های فعال.

jsx
import { useState } from 'react';

function Counter() {
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => setCount(count + 1)}>
                Increment
            </button>
        </div>
    );
}

useState چگونه کار می‌کند

مکانیزم داخلی useState بر اساس سیستم گره‌های fiber React است. هر کامپوننت در React با یک گره fiber نمایش داده می‌شود که لیست هوک‌های مرتبط با خود را به صورت لیست پیوندی ذخیره می‌کند. وقتی کامپوننت useState را فراخوانی می‌کند، React یک گره جدید در این لیست ایجاد کرده و مقدار فعلی state و ارجاع به صف به‌روزرسانی‌ها را در آن ذخیره می‌کند.

هنگام فراخوانی تابع setter، React state را بلافاصله به‌روزرسانی نمی‌کند. در عوض، به‌روزرسانی را در صف (pending state queue) قرار می‌دهد، رندر مجدد کامپوننت را برنامه‌ریزی می‌کند و فقط در طول رندر بعدی state جدید را بر اساس state قدیم و به‌روزرسانی‌های اعمال شده محاسبه می‌کند. این دسته‌بندی (batching) را تضمین می‌کند — اگر در یک handler رویداد سه بار setter را فراخوانی کنید، React آنها را در یک رندر مجدد گروه‌بندی می‌کند.

طبق React Team — React 18 Working Group (2024)، دسته‌بندی خودکار در React 18 گسترش یافت: اکنون به‌روزرسانی‌های state نه تنها در handlerهای رویداد، بلکه در setTimeout، callbackهای Promise و handlerهای بومی نیز گروه‌بندی می‌شوند. این باعث افزایش عملکرد تا 30% در سناریوهای با به‌روزرسانی‌های متعدد state شد.

به‌روزرسانی تابعی state

وقتی state جدید به state قبلی وابسته است، از فرم تابعی setter استفاده کنید: setCount(prev => prev + 1). React مقدار واقعی state را در لحظه اعمال به‌روزرسانی به تابع می‌دهد که صحت را حتی در به‌روزرسانی‌های دسته‌بندی شده تضمین می‌کند. بدون فرم تابعی setCount(count + 1) ممکن است از مقدار قدیمی استفاده کند اگر چند بار پشت سر هم فراخوانی شود.

jsx
// مقداردهی اولیه تنبل — فقط یک بار اجرا می‌شود
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
// Result: count increased by 3

// NOT batch-safe: stale closure
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// Result: count increased by 1 (stale closure)

نحو useState: اعلام و به‌روزرسانی

نحو پایه useState بسیار مختصر است: const [state, setState] = useState(initialValue). مقدار اولیه فقط در اولین رندر استفاده می‌شود؛ در رندرهای بعدی React آن را نادیده گرفته و مقدار ذخیره شده فعلی را برمی‌گرداند. اگر محاسبه اولیه نیاز به عملیات پرهزینه دارد، یک تابع مقداردهی اولیه (initializer) ارسال کنید: useState(() => computeExpensiveInitial()).

مقداردهی اولیه تنبل (lazy initial state) به ویژه زمانی مهم است که مقدار اولیه از localStorage، تبدیل داده پیچیده یا تجزیه پارامترهای URL به دست می‌آید. React تابع مقداردهی اولیه را فقط یک بار — هنگام mount کامپوننت — فراخوانی می‌کند که در منابع صرفه‌جویی می‌کند. بدون مقداردهی اولیه تنبل، عبارت پرهزینه در هر رندر محاسبه می‌شود حتی اگر نتیجه آن نادیده گرفته شود.

فرممثالچه زمانی استفاده کنیم
مستقیمuseState(0)مقدار اولیه ساده
تنبلuseState(() => compute())محاسبه اولیه پرهزینه
Setter تابعیsetState(prev => prev + 1)به‌روزرسانی بر اساس قبلی
Setter مستقیمsetState(newValue)مقدار جدید به قدیم وابسته نیست
jsx
// state شیء — ارجاع جدید ایجاد کنید
const [user, setUser] = useState(() => {
    const saved = localStorage.getItem('user');
    return saved ? JSON.parse(saved) : null;
});

useState با انواع مختلف داده

useState با انواع اولیه (اعداد، رشته‌ها، بولین) و انواع ارجاعی (آرایه‌ها، اشیاء) به یک خوبی کار می‌کند. اما یک تفاوت مهم وجود دارد: برای انواع اولیه React تغییر را با مقدار تشخیص می‌دهد، برای اشیاء و آرایه‌ها — با ارجاع. تغییر (mutate) شیء موجود بدون ایجاد شیء جدید باعث رندر مجدد نمی‌شود، زیرا ارجاع ثابت مانده است.

هنگام کار با اشیاء و آرایه‌ها همیشه یک کپی جدید با داده‌های تغییر یافته ایجاد کنید. برای اشیاء از نحو spread استفاده کنید: setUser(prev => ({...prev, name: newName})). برای آرایه‌ها — متدهایی که آرایه جدید برمی‌گردانند: filter, map, concat یا نحو spread برای اضافه کردن عناصر. متدهای تغییری مانند push, pop, splice مستقیماً کار نمی‌کنند — React آنها را نادیده می‌گیرد، زیرا ارجاع به آرایه تغییر نکرده است.

jsx
// ✅ درست: هوک‌ها در بالاترین سطح
const [form, setForm] = useState({ name: '', email: '' });
const updateField = (field, value) =>
    setForm(prev => ({ ...prev, [field]: value }));

// ...
const [items, setItems] = useState([]);
const addItem = item =>
    setItems(prev => [item, ...prev]);

قوانین استفاده از هوک‌ها با useState

مانند همه هوک‌های React، useState تابع دو قانون است. اول: هوک‌ها را فقط در بالاترین سطح کامپوننت فراخوانی کنید — نمی‌توان آنها را درون شرط‌ها، حلقه‌ها یا توابع تو در تو قرار داد. این تضمین می‌کند که هوک‌ها در هر رندر به ترتیب یکسان فراخوانی می‌شوند که برای لیست پیوندی داخلی React حیاتی است.

قانون دوم: هوک‌ها را فقط از کامپوننت‌های تابعی React یا هوک‌های سفارشی فراخوانی کنید. useState را از توابع معمولی، callbackها یا کامپوننت‌های کلاسی فراخوانی نکنید. نقض این قوانین منجر به خطای Invalid hook call می‌شود که React آن را در مرحله اجرا تشخیص می‌دهد.

  • نه در شرط‌ها — if (flag) { useState(0) } ترتیب فراخوانی هوک‌ها را نقض می‌کند.
  • نه در حلقه‌ها — for (...) { useState(0) } ترتیب را غیرقابل پیش‌بینی می‌کند.
  • نه در callbackها — useEffect(() => { useState(0) }, []) لیست پیوندی را خراب می‌کند.
jsx
// ❌ مشکل closure کهنه
function GoodComponent() {
    const [count, setCount] = useState(0);
    const [name, setName] = useState('');
    // ✅ با به‌روزرسانی تابعی رفع شد
}

// شمارنده قدیمی است!
function BadComponent() {
    if (isLogged) {
        const [user, setUser] = useState(null);
    }
}

اشتباهات رایج با useState

رایج‌ترین اشتباه — تغییر مستقیم اشیاء و آرایه‌ها به جای ایجاد ارجاع‌های جدید است. توسعه‌دهندگان به سبک تغییری از کامپوننت‌های کلاسی عادت کرده‌اند که در آن this.state.user.name = «نام جدید» کار می‌کرد (اگرچه توصیه نمی‌شد). در کامپوننت‌های تابعی چنین تغییری نادیده گرفته می‌شود: React تغییر ارجاع را نمی‌بیند و کامپوننت را دوباره رندر نمی‌کند.

  • تغییر شیء — user.name = «جدید» به جای setUser({...user, name: «جدید»}).
  • Closure کهنه — استفاده از مقدار قدیمی در setter بدون فرم تابعی.
  • رندرهای اضافی — فراخوانی setter با همان مقدار (React 18 برای انواع اولیه همچنان رندر می‌کند).
  • بازنشانی state — به‌روزرسانی ناخواسته prop کلید کامپوننت که آن را دوباره mount می‌کند.

مشکل closure کهنه (stale closure) زمانی رخ می‌دهد که callback ارسال شده به useEffect یا handler رویداد، مقدار قدیمی state را از closure در لحظه ایجاد callback «گرفته» باشد. راه حل — استفاده از فرم تابعی setter یا گنجاندن مقادیر مورد نیاز در آرایه وابستگی useEffect. طبق React Documentation — Hooks FAQ (2025)، closureهای کهنه دومین علت شایع باگ‌ها در هوک‌ها پس از آرایه وابستگی نادرست هستند.

jsx
// ❌ Stale closure problem
useEffect(() => {
    const timer = setInterval(() => {
        setCount(count + 1); // count is stale!
    }, 1000);
    return () => clearInterval(timer);
}, []);

// ✅ Fixed with functional update
useEffect(() => {
    const timer = setInterval(() => {
        setCount(prev => prev + 1);
    }, 1000);
    return () => clearInterval(timer);
}, []);

سوالات متداول

آیا می‌توان از useState برای اشیاء تو در تو پیچیده استفاده کرد؟

بله، اما با احتیاط. برای اشیاء عمیقاً تو در تو، هر سطح در به‌روزرسانی نیاز به کپی کردن دارد: setState(prev => ({...prev, nested: {...prev.nested, key: newValue}})). اگر اشیایی با سه سطح یا بیشتر تو رفتگی دارید، از Immer (کتابخانه به‌روزرسانی تغییرناپذیر) استفاده کنید یا state را به چند useState برای سطوح مختلف تقسیم کنید.

useState ناهمگام است یا همگام؟

useState از این نظر ناهمگام است که فراخوانی setter مقدار را بلافاصله به‌روزرسانی نمی‌کند. React به‌روزرسانی را در صف قرار داده و در رندر بعدی اجرا می‌کند. پس از setCount(newValue)، متغیر count در تابع فعلی همچنان مقدار قدیم را دارد. مقدار جدید فقط در فراخوانی بعدی کامپوننت در دسترس خواهد بود. مکانیزم دسته‌بندی تضمین می‌کند که فراخوانی‌های متعدد در یک رندر گروه‌بندی شوند.

اگر useState خارج از کامپوننت فراخوانی شود چه اتفاقی می‌افتد؟

React خطای Invalid hook call را پرتاب می‌کند. هوک‌ها فقط درون کامپوننت‌های تابعی React یا هوک‌های سفارشی (توابعی که نام آنها با use شروع می‌شود) قابل فراخوانی هستند. این محدودیت در پیاده‌سازی هوک‌ها تعبیه شده است: آنها از درخت fiber کامپوننت برای ذخیره state استفاده می‌کنند و خارج از کامپوننت این درخت در دسترس نیست.

چه زمانی از useState و چه زمانی از useReducer استفاده کنیم؟

useState برای stateهای ساده مستقل مناسب است (شمارنده‌ها، فیلدهای ورودی، پرچم‌ها). useReducer زمانی بهتر است که state یک شیء پیچیده با چندین فیلد باشد، منطق به‌روزرسانی به نوع عمل بستگی دارد یا state بعدی نیاز به تبدیل پیچیده دارد. قانون عملی: اگر بیش از سه فیلد state مرتبط دارید — از useReducer استفاده کنید.

چرا useState مقدار را بلافاصله به‌روزرسانی نمی‌کند؟

این کار برای عملکرد انجام شده است. اگر useState state را همگام به‌روزرسانی می‌کرد، هر فراخوانی setter باعث رندر مجدد فوری می‌شد که به رندرهای متعدد درون یک handler رویداد منجر می‌گردید. دسته‌بندی، که در آن React به‌روزرسانی‌ها را گروه‌بندی کرده و یک رندر مجدد انجام می‌دهد، بهینه‌سازی‌ای است که در React 16 معرفی و در React 18 با دسته‌بندی خودکار به طور قابل توجهی بهبود یافت.

خلاصه

  • useState — هوک پایه React برای مدیریت state محلی در کامپوننت‌های تابعی، بازگرداننده [state, setter].
  • تغییرناپذیری — state را نمی‌توان مستقیماً تغییر داد؛ همیشه مقدار جدید ارسال کنید یا از فرم تابعی setter استفاده کنید.
  • مقداردهی اولیه تنبل — برای محاسبات اولیه پرهزینه یک تابع ارسال کنید: useState(() => compute()).
  • Setter تابعی — setState(prev => prev + 1) صحت را در به‌روزرسانی‌های دسته‌بندی شده تضمین می‌کند.
  • دسته‌بندی — React فراخوانی‌های متعدد setter را در یک رندر مجدد برای بهینه‌سازی گروه‌بندی می‌کند.
  • قوانین هوک‌ها — useState را فقط در بالاترین سطح کامپوننت فراخوانی کنید، نه در شرط‌ها یا حلقه‌ها.
  • انواع داده — برای اشیاء و آرایه‌ها همیشه در به‌روزرسانی یک ارجاع جدید ایجاد کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید