آرایهها در جاوااسکریپت ماهیت تغییرپذیر (Mutable) دارند، اما وقتی آنها را در استیت React ذخیره میکنید، باید رفتارشان را کاملاً تغییرناپذیر (Immutable) فرض کنید. دقیقاً مشابه با اشیاء، هر زمان که قصد دارید آرایه موجود در استیت را بهروزرسانی کنید، نباید مستقیماً آیتمهای آن را دستکاری کنید؛ بلکه باید یک نسخه کپی یا یک آرایه جدید ساخته و استیت را با آن مقداردهی کنید.
نحوه اضافه، حذف یا ویرایش آیتمها در آرایه بدون شکستن قوانین React
روش صحیح بهروزرسانی اشیاءِ درون یک آرایه (آرایههای ساختاریافته)
استفاده از Immer برای بهینهسازی و کاهش کدهای تکراری آرایه
در جاوااسکریپت، آرایهها عملاً نوع خاصی از اشیاء هستند. دستوراتی مثل arr[0] = 'bird' یا متدهایی که ساختار اصلی آرایه را دستکاری میکنند (مانند push() و pop()) در استیت ریاکت ممنوع هستند.
در جدول زیر، فهرستی از متدهای رایج آرایه آمده است. هنگام مدیریت استیت در React، باید از متدهای ستون چپ دوری کرده و به جای آنها از متدهای غیرمخرب ستون راست (که یک آرایه جدید برگشت میدهند) استفاده کنید:
| نوع عملیات | ❌ دوری کنید (آرایه را دستکاری میکند) | استفاده کنید (آرایه جدید برمیگرداند) |
| افزودن (Adding) | push, unshift | concat یا نحو Spread [...arr] |
| حذف کردن (Removing) | pop, shift, splice | filter, slice |
| جایگزینی (Replacing) | splice یا انتساب مستقیم arr[i] = ... | map |
| مرتبسازی (Sorting) | reverse, sort | ابتدا کپی بگیرید، سپس متد را صدا بزنید |
⚠️ یک اشتباه رایج در نامگذاری: > متدهای
sliceوspliceنامهای بسیار مشابهی دارند اما کارکردشان کاملاً متفاوت است:
متد
slice(بدون p) یک تکه از آرایه را کپی کرده و آرایه جدید میدهد (کاملاً مجاز).متد
splice(با p) آرایه اصلی را تکهتکه کرده و آن را دستکاری میکند (ممنوع).
استفاده از push() آرایه اصلی استیت را دچار جهش (Mutation) میکند. روش اصولی، بهرهگیری از نحو Array Spread (...) است:
// اضافه کردن به انتهای آرایه (مشابه push)
setArtists([
...artists, // تمام آیتمهای قدیمی را پهن کن
{ id: nextId++, name: name } // و این آیتم جدید را به انتها بچسبان
]);
اگر میخواهید آیتم جدید به ابتدای آرایه اضافه شود (مشابه رفتار unshift)، کافی است جای المان جدید و اپراتور اسپردا را عوض کنید:
// اضافه کردن به ابتدای آرایه (مشابه unshift)
setArtists([
{ id: nextId++, name: name },
...artists
]);
راحتترین راه برای حذف یک المان، استفاده از متد filter() است. این متد بدون تغییر دادن آرایه اصلی، یک آرایه جدید صافشده تولید میکند:
const [artists, setArtists] = useState(initialArtists);
function handleDelete(artistId) {
// آرایهای بساز شامل تمام هنرمندانی که آیدی آنها برابر با artistId نیست
setArtists(
artists.filter(a => a.id !== artistId)
);
}
اگر میخواهید ساختار یا مقادیر برخی از اعضای آرایه را تغییر دهید، متد map() بهترین گزینه است. برای مثال اگر آرایهای از اشکال هندسی دارید و میخواهید فقط دایرهها را ۵۰ پیکسل به پایین جابجا کنید:
function handleMoveCircles() {
const nextShapes = shapes.map(shape => {
if (shape.type === 'square') {
return shape; // بدون تغییر برگشت داده میشود
} else {
return {
...shape, // کپی شیء دایره
y: shape.y + 50 // فقط فیلد y را تغییر بده
};
}
});
setShapes(nextShapes);
}
اگر قصد دارید فیلد یا مقداری را در یک ایندکس (Index) خاص جایگزین کنید، باز هم از map() استفاده کنید. آرگومان دوم کلبکِ map به شما ایندکس فعلی را میدهد:
function handleIncrementClick(index) {
const nextCounters = counters.map((counter, i) => {
if (i === index) {
return counter + 1; // مقدار ایندکس کلیکشده را افزایش بده
} else {
return counter; // بقیه بدون تغییر میمانند
}
});
setCounters(nextCounters);
}
گاهی نیاز است المانی را دقیقاً در یک موقعیت میانی (نه ابتدا و نه انتها) درج کنید. ترکیب متد slice() و نحو اسپرد این کار را ممکن میسازد:
function handleInsert(name) {
const insertAt = 1; // فرض کنید میخواهیم در ایندکس ۱ درج کنیم
const nextArtists = [
...artists.slice(0, insertAt), // کپی المانهای قبل از نقطه درج
{ id: nextId++, name: name }, // درج المان جدید
...artists.slice(insertAt) // کپی المانهای بعد از نقطه درج
];
setArtists(nextArtists);
}
متدهای sort() و reverse() آرایه اصلی را دستکاری میکنند. راهکار اصولی این است که ابتدا یک کپی سطحی از آرایه بگیرید و سپس متدهای مذکور را روی نسخه کپی اجرا کنید:
function handleReverse() {
const nextList = [...list]; // ابتدا یک کپی مجزا بساز
nextList.reverse(); // حالا متد مخرب را روی کپی اعمال کن (بیخطر)
setList(nextList);
}
حتی اگر با ساختار [...list] از یک آرایه کپی بگیرید، باز هم اجازه ندارید اشیاء درون آن را مستقیماً دستکاری کنید! چرا؟ چون کپی کردن با نحو اسپرد، سطحی (Shallow) است؛ یعنی اشیاء درون آرایه جدید همچنان به همان خانه حافظهای اشاره میکنند که اشیاء آرایه قدیمی اشاره میکردند.
به این کد باگدار نگاه کنید:
const nextList = [...list];
nextList[0].seen = true; // ❌ اشتباه بزرگ! شیء اصلی موجود در استیت دستکاری شد.
setList(nextList);
با اینکه nextList و list دو آرایه متفاوت هستند، اما nextList[0] و list[0] هر دو به یک شیء مشترک در حافظه ارجاع دارند. تغییر دادن فیلد seen در آرایه کپی، ناخواسته استیتهای دیگر (مثلا لیستهای اشتراکی) را هم خراب میکند.
mapبرای حل این مشکل، هنگام بهروزرسانی شیء درون آرایه، باید خود آن شیء را هم کپی کنید:
setMyList(myList.map(artwork => {
if (artwork.id === artworkId) {
// یک شیء کاملاً جدید با تغییرات مشخص بساز و کپی کن
return { ...artwork, seen: nextSeen };
} else {
return artwork; // بدون تغییر عبور بده
}
}));
اگر آرایههای تودرتو دارید یا مدیریت کپیهای لایه به لایه با map کدهای شما را طولانی و پیچیده کرده است، از Immer استفاده کنید. با Immer ظاهر کدهای شما کاملاً شبیه به متدهای دستکاری مستقیم (Mutation) میشود، اما ساختار زیرین کدهای شما ۱۰۰٪ ایمن و تغییرناپذیر باقی میماند:
import { useImmer } from 'use-immer';
export default function BucketList() {
const [myList, updateMyList] = useImmer(initialList);
function handleToggleMyList(artworkId, nextSeen) {
updateMyList(draft => {
// پیدا کردن شیء درون پیشنویس (Draft) ایمن Immer
const artwork = draft.find(a => a.id === artworkId);
artwork.seen = nextSeen; // کاملاً مجاز و ایمن!
});
}
// ...
}
با Immer شما حتی میتوانید از متدهای مخرب مثل push()، pop() یا splice() نیز روی شیء draft استفاده کنید، زیرا Immer در پشت صحنه تغییرات را ضبط کرده و یک نسخه نهایی شسته و رفته برای ریاکت میسازد.
میتوانید آرایهها را در استیت قرار دهید، اما هرگز نباید آنها را مستقیماً تغییر دهید.
به جای دستکاری آرایه، نسخه جدیدی از آن را ساخته و استیت را بهروزرسانی کنید.
از ساختار اسپرد [...arr, newItem] برای افزودن اعضای جدید استفاده کنید.
متدهای غیرمخرب filter() و map() بهترین ابزارها برای حذف یا تغییر اجزای آرایه در React هستند.
کپی کردن آرایه به صورت سطحی (Shallow) است؛ برای تغییر اشیاء داخل آرایه، باید کل شیء هدف را هم با نحو اسپرد کپی کنید.
برای خلاصه کردن کدهای آرایههای پیچیده، کتابخانه Immer بهترین راهحل پیشرو است.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://react.dev/learn