افزودن تعامل پذیری

برخی از عناصر روی صفحه به تعامل کاربر پاسخ میدهند. به عنوان مثال، کلیک بر روی یک گالری عکس تصویر فعال را تغییر میدهد. در React، دادههایی که در طول زمان تغییر میکنند state (وضعیت) نامیده میشوند. شما میتوانید به هر کامپوننت، state اضافه کنید و آن را در صورت نیاز بهروز کنید. در این فصل، یاد خواهید گرفت که چگونه کامپوننتهایی بنویسید که تعاملات را مدیریت کنند، state خود را بهروزرسانی کنند و خروجیهای مختلف را در طول زمان نمایش دهند.
پاسخ به رویدادها
ری اکت به شما اجازه میدهد که مدیریت رویدادها را به JSX خود اضافه کنید. مدیریت رویداد توابعی هستند که در پاسخ به تعاملات کاربر مانند کلیک، آمدن ماوس روی المان، تمرکز روی ورودیهای فرم و... فعال میشوند.
کامپوننتهای داخلی مانند <button>
فقط از رویدادهای داخلی مرورگر مانند onClick
پشتیبانی میکنند. با این حال، شما میتوانید کامپوننتهای خود را ایجاد کنید و نامهای دلخواه مخصوص به رویدادهای آنها را به ورودی مربوطه بدهید.
مشاهده کد نمونه در CodeSandbox
خواندن پاسخ به رویدادها را از دست ندهید تا بیاموزید چگونه مدیریت رویداد را به کدتان اضافه کنید.
وضعیت: حافظه یک کامپوننت
کامپوننتها معمولاً نیاز دارند که چیزی که روی صفحه نمایش داده میشود را بر اساس تعامل تغییر دهند. وارد کردن متن در فرم باید فیلد ورودی را بهروزرسانی کند، کلیک روی "بعدی" در یک گالری تصاویری، تصویر نمایش داده شده را تغییر میدهد و کلیک روی "خرید"، یک محصول را به سبد خرید اضافه میکند. کامپوننتها باید داده ها را "به خاطر بسپارند": مقدار ورودی فعلی، تصویر جاری، سبد خرید. در React، این نوع حافظه خاص کامپوننت state (وضعیت) نامیده میشود.
شما میتوانید با استفاده از هوک useState
وضعیت را به یک کامپوننت اضافه کنید. هوک ها توابع خاصی هستند که به کامپوننتهای شما اجازه میدهند که از ویژگیهای React (state یکی از این ویژگیهاست) استفاده کنند. هوک useState
به شما این امکان را میدهد که یک متغیر state اعلام کنید. این Hook حالت اولیه را میگیرد و یک جفت مقدار برمیگرداند: state فعلی و یک تابع تنظیمکننده state که به شما این امکان را میدهد آن را بهروزرسانی کنید.
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
برای مشاهده چگونگی استفاده و بهروزرسانی state در یک گالری عکس کد زیر را مشاهده کنید:
برای یادگیری نحوه "به خاطر سپردن" یک مقدار و بهروزرسانی آن در زمان تعامل کاربر، خواندن وضعیت: حافظه یک کامپوننت را فراموش نکنید.
رندر و تأیید
قبل از اینکه کامپوننتهای شما روی صفحه نمایش داده شوند، باید توسط React رندر شوند. درک مراحل این فرایند به شما کمک میکند که درک خوبی در مورد چگونگی اجرای کد و رفتار آن را داشته باشید.
تصور کنید که کامپوننتهای شما مانند آشپزهایی در آشپزخانه هستند که غذاهای خوشمزهای را از مواد اولیه تهیه میکنند. در این سناریو، React پیشخدمت است که درخواستها را از مشتریان میگیرد و آنها را به سفارشهایشان میرساند. این فرایند درخواست و سرویس UI شامل سه مرحله است:
- فعال کردن یک رندر (تحویل سفارش مهمان به آشپزخانه)
- رندر کردن کامپوننت (آماده کردن سفارش در آشپزخانه)
- تأیید در DOM (قرار دادن سفارش بر روی میز)
برای یادگیری چرخه عمر بهروزرسانی UI، خواندن رندر و تأیید را از دست ندهید.
وضعیت به عنوان یک عکس فوری
برخلاف متغیرهای معمولی جاوااسکریپت، state در React بیشتر شبیه یک عکس فوری عمل میکند. تعیین آن باعث تغییر متغیر state فعلی شما نمیشود، بلکه یک رندر مجدد را فعال میکند. این ممکن است در ابتدا تعجبآور باشد!
console.log(count); // 0
setCount(count + 1); // Request a re-render with 1
console.log(count); // Still 0!
این رفتار به شما کمک میکند تا از خطاهای جزئی اجتناب کنید. در اینجا یک اپلیکیشن چت کوچک برای شما قرار داده شده است. سعی کنید حدس بزنید اگر اول دکمه "ارسال" را فشار داده و سپس گیرنده را به باب تغییر دهید چه اتفاقی خواهد افتاد. نام چه کسی پنج ثانیه بعد در alert
ظاهر خواهد شد؟
مشاهده کد و اجرای آن در CodeSanbox
برای یادگیری اینکه چرا وضعیت در درون مدیریت رویداد "ثابت" و تغییرناپذیر به نظر میرسد، خواندن state به عنوان یک عکس فوری را فراموش نکنید.
صف کردن مجموعهای از بهروزرسانیهای وضعیت
این کامپوننت باگ دارد: کلیک بر روی "+3" تنها یک بار، امتیاز را افزایش میدهد. مشاهده کد و اجرای آن در CodeSandbox
import { useState } from 'react';
export default function Counter() {
const [score, setScore] = useState(0);
function increment() {
setScore(score + 1);
}
return (
<>
<button onClick={() => increment()}>+1</button>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<h1>Score: {score}</h1>
</>
)
}
State به عنوان یک عکس فوری توضیح میدهد که چرا این اتفاق میافتد. تعیین وضعیت، درخواست یک رندر مجدد جدید ایجاد میکند، اما در کدی که در حال حاضر در حال اجراست، تغییری ایجاد نمی کند. بنابراین، score
بلافاصله پس از فراخوانی setScore(score + 1)
همان 0
باقی میماند.
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
شما میتوانید این مشکل را با پاس دادن یک تابع بهروزرسانی هنگام تنظیم وضعیت اصلاح کنید. توجه داشته باشید که چگونه جایگزینی setScore(score + 1)
با setScore(s => s + 1)
دکمه "+3" را اصلاح میکند. این به شما این امکان را میدهد که چندین بهروزرسانی وضعیت را در صف قرار دهید.
مشاهده کد اصلاح شده و نتیجه آن در CodeSandbox
import { useState } from 'react';
export default function Counter() {
const [score, setScore] = useState(0);
function increment() {
setScore(s => s + 1);
}
return (
<>
<button onClick={() => increment()}>+1</button>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<h1>Score: {score}</h1>
</>
)
}
برای یادگیری نحوه صف کردن چندین بهروزرسانیهای وضعیت، خواندن صف کردن مجموعهای از بهروزرسانیهای وضعیت را فراموش نکنید.
بهروزرسانی اشیا در وضعیت
وضعیت میتواند هر نوع مقداری از جاوااسکریپت، از جمله اشیا، را نگهداری کند. اما شما نباید به صورت مستقیم اشیا و آرایههایی که در وضعیت نگهداری می شوند را تغییر دهید. به جای آن، زمانی که میخواهید یک شی یا آرایه را بهروزرسانی کنید، باید ابتدا شی ای جدید ایجاد کنید (یا یک کپی از شی موجود بگیرید) و سپس وضعیت را با استفاده از آن بهروز کنید.
معمولاً از سینتکس گسترش ...
برای کپی کردن اشیا و آرایههایی که میخواهید تغییر دهید، استفاده خواهید کرد. برای مثال، بهروزرسانی یک شی تو در تو میتواند به شکل زیر باشد:
مشاهده کد و اجرای آن در CodeSandbox
import { useState } from 'react';
export default function Form() {
const [person, setPerson] = useState({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
});
function handleNameChange(e) {
setPerson({
...person,
name: e.target.value
});
}
function handleTitleChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
title: e.target.value
}
});
}
function handleCityChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
city: e.target.value
}
});
}
function handleImageChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
image: e.target.value
}
});
}
return (
<>
<label>
Name:
<input
value={person.name}
onChange={handleNameChange}
/>
</label>
<label>
Title:
<input
value={person.artwork.title}
onChange={handleTitleChange}
/>
</label>
<label>
City:
<input
value={person.artwork.city}
onChange={handleCityChange}
/>
</label>
<label>
Image:
<input
value={person.artwork.image}
onChange={handleImageChange}
/>
</label>
<p>
<i>{person.artwork.title}</i>
{' by '}
{person.name}
<br />
(located in {person.artwork.city})
</p>
<img
src={person.artwork.image}
alt={person.artwork.title}
/>
</>
);
}
اگر کپی کردن مقدار اشیا در کد تکراری و خستهکننده شده است، میتوانید از یک کتابخانه مانند Immer برای کاهش نوشتن کد تکراری استفاده کنید:
مشاهده کد واجرای آن در CodeSandbox
import { useImmer } from 'use-immer';
export default function Form() {
const [person, updatePerson] = useImmer({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
});
function handleNameChange(e) {
updatePerson(draft => {
draft.name = e.target.value;
});
}
function handleTitleChange(e) {
updatePerson(draft => {
draft.artwork.title = e.target.value;
});
}
function handleCityChange(e) {
updatePerson(draft => {
draft.artwork.city = e.target.value;
});
}
function handleImageChange(e) {
updatePerson(draft => {
draft.artwork.image = e.target.value;
});
}
return (
<>
<label>
Name:
<input
value={person.name}
onChange={handleNameChange}
/>
</label>
<label>
Title:
<input
value={person.artwork.title}
onChange={handleTitleChange}
/>
</label>
<label>
City:
<input
value={person.artwork.city}
onChange={handleCityChange}
/>
</label>
<label>
Image:
<input
value={person.artwork.image}
onChange={handleImageChange}
/>
</label>
<p>
<i>{person.artwork.title}</i>
{' by '}
{person.name}
<br />
(located in {person.artwork.city})
</p>
<img
src={person.artwork.image}
alt={person.artwork.title}
/>
</>
);
}
برای یادگیری نحوه بهروزرسانی صحیح اشیا، خواندن بهروزرسانی اشیا در state را فراموش نکنید.
بهروزرسانی آرایهها در وضعیت
آرایهها نوع دیگری از اشیای قابل تغییر جاوااسکریپت هستند که میتوانید در وضعیت ذخیره کنید و باید به آنها به عنوان دادههای فقط خواندنی نگاه کنید. درست مانند اشیا، زمانی که میخواهید یک آرایه را که در وضعیت نگهداری میشود بهروزرسانی کنید، باید یک آرایه جدید ایجاد کنید (یا یک کپی از آرایه موجود بگیرید) و سپس وضعیت را برای استفاده از آرایه جدید تنظیم کنید:
مشاهده کد و اجرای آن در CodeSandbox
import { useState } from 'react';
const initialList = [
{ id: 0, title: 'Big Bellies', seen: false },
{ id: 1, title: 'Lunar Landscape', seen: false },
{ id: 2, title: 'Terracotta Army', seen: true },
];
export default function BucketList() {
const [list, setList] = useState(
initialList
);
function handleToggle(artworkId, nextSeen) {
setList(list.map(artwork => {
if (artwork.id === artworkId) {
return { ...artwork, seen: nextSeen };
} else {
return artwork;
}
}));
}
return (
<>
<h1>Art Bucket List</h1>
<h2>My list of art to see:</h2>
<ItemList
artworks={list}
onToggle={handleToggle} />
</>
);
}
function ItemList({ artworks, onToggle }) {
return (
<ul>
{artworks.map(artwork => (
<li key={artwork.id}>
<label>
<input
type="checkbox"
checked={artwork.seen}
onChange={e => {
onToggle(
artwork.id,
e.target.checked
);
}}
/>
{artwork.title}
</label>
</li>
))}
</ul>
);
}
اگر کپی کردن آرایهها در کد تکراری و خستهکننده شده است، میتوانید از یک کتابخانه ای مانند Immer برای کاهش نوشتن کد تکراری استفاده کنید:
مشاهده کد و نحوه اجرای آن در CodeSandbox
import { useState } from 'react';
import { useImmer } from 'use-immer';
const initialList = [
{ id: 0, title: 'Big Bellies', seen: false },
{ id: 1, title: 'Lunar Landscape', seen: false },
{ id: 2, title: 'Terracotta Army', seen: true },
];
export default function BucketList() {
const [list, updateList] = useImmer(initialList);
function handleToggle(artworkId, nextSeen) {
updateList(draft => {
const artwork = draft.find(a =>
a.id === artworkId
);
artwork.seen = nextSeen;
});
}
return (
<>
<h1>Art Bucket List</h1>
<h2>My list of art to see:</h2>
<ItemList
artworks={list}
onToggle={handleToggle} />
</>
);
}
function ItemList({ artworks, onToggle }) {
return (
<ul>
{artworks.map(artwork => (
<li key={artwork.id}>
<label>
<input
type="checkbox"
checked={artwork.seen}
onChange={e => {
onToggle(
artwork.id,
e.target.checked
);
}}
/>
{artwork.title}
</label>
</li>
))}
</ul>
);
}
برای یادگیری نحوه بهروزرسانی صحیح آرایهها، خواندن بهروزرسانی آرایهها در state را فراموش نکنید.
قدم بعدی چیست؟
برای شروع مطلب پاسخ به رویدادها را مطالعه کنید!
یا اگر با این موضوعات آشنا هستید، به سراغ مدیریت state بروید