حالت یا State در React به چه معنا است؟ – به زبان ساده
State یک شیء جاوا اسکریپت است که دادههای دینامیک کامپوننت را نگهداری میکند و آن را قادر میسازد که تغییرات بین رندرها را ردگیری کند. از آنجا که State دینامیک است، تنها به منظور ایجاد تعاملپذیری استفاده میشود و از این رو در مورد پروژههای استاتیک React کاربردی ندارد.
کامپوننتها به صورت کلاسهایی تعریف میشوند که ویژگیهای اضافی دارند. State محلی دقیقاً به معنای یک ویژگی است که تنها در کلاسها وجود دارد. State صرفاً درون کلاس میتواند مورد استفاده قرار گیرد و معمولاً تنها جایی است که میتوان this.state را به عنوان سازنده مطرح کرد.
class Greeting extends React.Component { constructor() { super(); this.state = { name: 'John Smith' } } render() { return <h1>Hello, my name is { this.state.name }</h1>; } }
امروزه میتوان از State بدون سازنده و از طریق «مقداردهی اولیه مشخصه» (Property initializers) استفاده کرد که ویژگی جدیدی محسوب میشود.
class Greeting extends React.Component { state = { name: 'John Smith' } } render() { return <h1>Hello, my name is { this.state.name }</h1>; } }
State درون کامپوننت مدیریت میشود، همان طور که متغیرها درون یک تابع اعلان میشوند. State در کامپوننت React در واقع حالت محلی خودش است، یعنی حالت نمیتواند خارج از کامپوننت مورد دسترسی یا تغییر قرار گیرد و تنها درون آن کاربرد دارد. این وضعیت شبیه تابعی است که حیطه محلی خود را دارد.
setState چه نقشی دارد؟
()setState یک بهروزرسانی برای شیء state کامپوننت را زمانبندی میکند. زمانی که حالت تغییر یابد، کامپوننت از طریق رندرگیری مجدد پاسخ میدهد.
استفاده صحیح از State
- State را به صورت مستقیم دستکاری نکنید.
- بهروزرسانیهای State میتوانند ناهمگام باشند.
- بهروزرسانیهای State ادغام میشوند.
در ادامه هر یک از موارد فوق را توضیح میدهیم.
State را به صورت مستقیم دستکاری نکنید
منظور از این نکته آن است که نباید state را به صورت مستقیم تغییر داد، زیرا مانند مثال زیر باعث میشود که کامپوننت به صورت خودکار رندرگیری مجدد نشود:
// Wrong this.state.comment = 'Hello';
در عوض باید از آن به صورت زیر استفاده کرد:
// Correct this.setState({comment: 'Hello'});
و همچنان که قبلاً اشاره کردیم، تنها جایی که میتوان this.state را انتساب داد در سازنده (constructor) است.
بهروزرسانیهای State میتوانند ناهمگام باشند
React ممکن است چندین فراخوانی ()setState را به منظور افزایش عملکرد با هم در یک بهروزرسانی تجمیع کند.
فراخوانیهای setState زمانی که درون «دستگیرههای رویداد» (event handlers) قرار دارند، ناهمگام هستند. در این حالت ما از this.state برای نشان دادن مقدار جدید بلافاصله پس از فراخوانی setState استفاده نمیکنیم.
incrementCount() { // Note: this will *not* work as intended. this.setState({count: this.state.count + 1}); } handleSomething() { // Let's say `this.state.count` starts at 0. this.incrementCount(); this.incrementCount(); this.incrementCount(); // When React re-renders the component, `this.state.count` will be 1, but you expected 3. // This is because `incrementCount()` function above reads from `this.state.count`, // but React doesn't update `this.state.count` until the component is re-rendered. // So `incrementCount()` ends up reading `this.state.count` as 0 every time, and sets it to 1.
برای اصلاح این وضعیت باید از شکل دوم setState استفاده کنیم که به جای یک شیء؛ تابعی را میپذیرد تا مطمئن شوید که فراخوانی همواره از جدیدترین نسخه از state استفاده میکند.
ارسال یک تابع بهروزرسانی کننده، امکان دسترسی به مقدار کنونی حالت را درون updater فراهم میسازد. از آنجا که setState تجمیع میشود، این شرایط اجازه میدهد که بهروزرسانیها به هم زنجیر شوند و تضمین شود که به جای تداخل؛ بر روی همدیگر قرار گرفتهاند:
incrementCount() { this.setState((state) => { // Important: read `state` instead of `this.state` when updating. return {count: state.count + 1} }); } handleSomething() { // Let's say `this.state.count` starts at 0. this.incrementCount(); this.incrementCount(); this.incrementCount(); // If you read `this.state.count` now, it would still be 0. // But when React re-renders the component, it will be 3. }
ضمناً میتوان props را به عنوان آرگومان دوم در زمان اعمال بهروزرسانی ارسال کرد:
// Correct this.setState((state, props) => ({ counter: state.counter + props.increment }));
بهروزرسانیهای حالت ادغام میشوند
زمانی که ()setState فراخوانی میشود، ریاکت شیئی را که به حالت جاری ارسال کردهاید ادغام میکند. برای نمونه حالت میتواند شامل چندین متغیر مستقل از هم باشد:
constructor(props) { super(props); this.state = { posts: [], comments: [] }; }
در این صورت میتوان آنها را به صورت مستقل از هم با فراخوانیهای مجزای ()setSatet بهروزرسانی کرد:
componentDidMount() { fetchPosts().then(response => { this.setState({ posts: response.posts }); }); fetchComments().then(response => { this.setState({ comments: response.comments }); }); }
در این حالت، ادغام کردن به صورت سطحی رخ میدهد و از این رو ({this.setState({comments باعث میشود this.state.posts دستنخورده بماند؛ اما به طور کامل جایگزین this.state.comments میشود.
ممکن است بپرسید که ()ComponentDidMount چیست و چگونه استفاده میشود. این متدی است که بیدرنگ پس از سوار شدن کامپوننت (یعنی درج آن در درخت) فراخوانی میشود. مقداردهی اولیه که نیازمند گرههای DOM است باید در این جا صورت بگیرد. بنابراین اگر میخواهید دادههایی را از یک نقطه انتهایی ریموت (API) بارگذاری کنید، این جا محل خوبی برای مقداردهی اولیه درخواست شبکه است.
همان طور که در مثال فوق میبینید، مشخصات this.state اینک مستقل هستند و به صورت مجزا به this.setState اضافه میشوند، یک شیء به هر مشخصه ارسال میشود و به وسیله ()ComponentDidMount احاطه شده است که پس از دریافت پاسخی از ()etchPosts و ()fetchComments به درخت DOM اضافه خواهد شد.
گردش رو به پایین دادهها
نه کامپوننتهای والد و نه کامپوننتهای فرزند نمیتوانند بدانند که یک کامپوننت خاص با حالت (stateful) یا بی حالت (stateless) است و اهمیتی هم نمیدهند که حالت به صورت تابع یا کلاس تعریف شده است. این مشخصات جز برای کامپوننتی ک مالک است و آن را تنظیم میکند قابل دسترسی نیستند. به همین دلیل است که state در اغلب موارد، محلی (local) یا کپسوله (encapsulated) نامیده میشود.
یک کامپوننت میتواند انتخاب کند که حالت خود را به صورت props به کامپوننتهای فرزندش ارسال کند. این وضعیت برای کامپوننتهای تعریف شده از سوی کاربر نیز عمل میکند:
<FormattedDate date={this.state.date} />
کامپوننت FormattedDate میتواند date را در props خود دریافت کند و نمیتواند بداند که از حالت Clock یا از props آن آمده است و یا به صورت دستی وارد شده است:
function FormattedDate(props) { return <h2>It is {props.date.toLocaleTimeString()}.</h2>; }
این وضعیت گردش دادههای بالا به پایین یا غیر جهتدار نامیده میشود. هر حالتی همواره از سوی کامپوننت خاصی مالکیت میشود و هر داده یا UI که از آن حالت مشتق شده باشد تنها بر روی کامپوننتهای زیر آن کامپوننت در درخت مؤثر خواهد بود.
کامپوننتها به واقع ایزوله هستند و به صورت مستقل از هم بهروزرسانی میشوند.
حالت اختیاری است. از آنجا که state باعث افزایش پیچیدگی و کاهش بهرهوری میشود ترجیح کلی بر استفاده از کامپوننتهای بدون state است. با این که در اپلیکیشنهای تعاملپذیر به استفاده از state نیاز داریم؛ اما باید از استفاده بیش از حد از کامپوننتهای با حالت اجتناب کنید.
اگر این مطلب برایتان مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای طراحی و توسعه پروژه های وب
- آموزش جاوا اسکریپت (JavaScript)
- مجموعه آموزشهای برنامهنویسی
- آموزش React.js در کمتر از ۵ دقیقه — از صفر تا صد
- آموزش تعریف توابع در جاوا اسکریپت (JavaScript)
==
کلمه ی
state
که نباید ترجمه بشه بلکه به همین شکل داخل متن فارسی بکار برده بشه