state بخش مهمی از یک برنامه React است و بیشتر قابلیتها شامل بهروزرسانی state در کامپوننتها هستند. اما هر چه برنامه بزرگتر میشود بهروزرسانیهای state نیز پیچیدهتر میشوند. روش متفاوتی برای مدیریت بهروزرسانیهای state وجود دارد، و آن استفاده از reducerها است. اما reducer چیست؟ چطور باید از آنها استفاده کنیم؟ هوک useReducer چه کاری در برنامه ما انجام میدهد؟
در این مقاله قصد داریم تا همه این موارد را بررسی کرده و پاسخ سوالات را بدهیم.
مثالی از یک برنامه To-Do را باهم بررسی میکنیم. این برنامه شامل افزودن، حذف و بهروزرسانی موارد موجود در todo لیست است. خود عملیات بهروزرسانی ممکن است شامل بهروزرسانی و ایجاد تغییر در یک تسک و یا علامتگذاری آن به عنوان تسک تکمیل شده باشد.
هنگامی که ما یک todo لیست را پیادهسازی میکنیم، یک متغیر state به نام todoList
داریم که انجام هر عملیات و بهروزرسانیهای state را به کمک آن انجام خواهیم داد. با این حال، این بهروزرسانیهای state ممکن است در قسمتهای مختلف ظاهر شوند، حتی گاهی اوقات این اتفاق در داخل کامپوننت صورت نمیگیرد.
برای اینکه کدی که داریم خوانایی بیشتری داشته باشد میتوانیم تمام بهروزرسانیهای state را به یک تابع جدا منتقل کنیم که خارج از کامپوننت مورد نظر ما قرار دارد. بنابراین در حین انجام عملیات مورد نیاز، کامپوننت ما فقط باید یک متد را فراخوانی کرده و عملیاتی که میخواهیم انجام شود را انتخاب کند.
تابعی که شامل تمام بهروزرسانیهای state ما است، reducer نامیده میشود. این به این دلیل است که ما منطق state را به یک تابع جداگانه کاهش میدهیم. متدی که برای انجام عملیات فراخوانی میکنیم، متد dispatch
است.
میتوانیم با استفاده از هوک useReducer یک reducer به کامپوننت خود اضافه کنیم. اکنون متد useReducer را از کتابخانه React به صورت زیر import میکنیم:
import { useReducer } from 'react'
متد useReducer یک متغیر state و یک متد dispatch
برای ایجاد تغییرات state به ما میدهد. state را میتوانیم به صورت زیر تعریف کنیم:
const [state, dispatch] = useReducer(reducerMethod, initialValue)
متد reducer شامل منطق state است. ما میتوانیم با استفاده از متد dispatch
، منطق state را انتخاب کنیم. state همچنین میتواند مقداری اولیه مشابه با هوک useState داشته باشد.
برای این کار یک مثال ساده که در آن لیستی از کاربران داریم را باهم بررسی میکنیم. در این مثال میتوانیم یک کاربر جدید اضافه کنیم، یک کاربر موجود را حذف کنیم و یا این که جزئیات مربوط به یک کاربر را بهروزرسانی کنیم. معمولا برای انجام این کار یک متغیر state به نام user
ایجاد میکنیم و بهروزرسانیهای state در قسمتهای مختلف را انجام میدهیم.
اکنون میخواهیم سعی کنیم همین کار را با استفاده از reducerها انجام دهیم:
const [users, dispatch] = useReducer(reducerMethod, userData);
برای این کار از دادههای اولیه زیر استفاده میکنیم:
const userData = [ { id:1, name: 'kunal', age: 22, admin: true }, { id:2, name: 'rounak', age: 23, admin: false }, { id:3, name: 'utkarsh', age: 22, admin: false }, ]
متد reducer شامل بهروزرسانیهای state است. این متد دو آرگومان میگیرد. آرگومان اول مقدار فعلی و آرگومان دوم یک آبجکت action است. آبجکت action شامل نوع action و دادههای اضافی مورد نیاز برای انجام بهروزرسانی میباشد.
ما سه نوع بهروزرسانی انجام خواهیم داد: افزودن کاربر جدید، بهروزرسانی کاربر و حذف آن. برای انتخاب نوع عملیاتی که باید انجام شود، از switch-case استفاده میکنیم.
const reducerMethod = (users, action) => { switch(action.type) { // State updates here } }
فیلد type
حاوی نام عملیاتی است که باید انجام شود. این یک رشته است و میتوانیم هر مقداری را که میخواهیم تنظیم کنیم. فقط باید مطمئن شویم که برای خوانایی بهتر با action انجام شده مرتبط باشد. ابتدا عملیات افزودن را انجام میدهیم:
case 'addUser': { return [ ...users, action.newUser ] }
منطق بهروزرسانی state مشابه setState است. در این مثال به جای ایجاد تغییرات مستقیم در متغیر state، یک مقدار state جدید return میکنیم.
اکنون عملیات بهروزرسانی را انجام میدهیم. در حین انجام عملیات بهروزرسانی، متد dispatch یک آبجکت updatedUser
را برای بهروزرسانی کاربر موجود ارسال میکند. این دادههای اضافی از طریق آبجکت action
ارسال میشود.
case 'updateUser': { return users.map(user => { if(user.id == action.updatedUser.id) return action.updatedUser return user; }) }
در این مرحله برای عملیات حذف، متد dispatch
فقط id
آبجکت را ارسال میکند تا آرایه state بتواند آن را فیلتر کند.
case 'deleteUser': { return users.filter(user => user.id !== action.id) }
اگر بخواهیم یک مورد پیشفرض برای عملی غیر از موارد مشخصشده داشته باشیم، به صورت زیر عمل میکنیم:
default: { // Handle error here }
در ادامه قصد داریم تا کامپوننتهایی که در واقع از این reducer استفاده میکنند را بسازیم.
لیستی از کاربران با ویژگیهای زیر را در کامپوننت UserDetails
نمایش میدهیم:
<UsersList users={users} handleUpdateUser={handleUpdateUser} handleDeleteUser={handleDeleteUser} />
همچنین یک فرم برای افزودن کاربران جدید در کامپوننت AddUserForm
ایجاد میکنیم.
<AddUserForm handleAddUser={handleAddUser} />
ما در در این مثال به پیادهسازی واقعی کامپوننتها اشاره نکردهایم، زیرا تمرکز اصلی فقط بر روی قسمت بهروزرسانی state است.
بهروزرسانیهای state را با فراخوانی متد dispatch
و ارسال type بهروزرسانی state با مقداری داده، در متدهای handler انجام میدهیم. همینطور کاربر جدیدی که قرار است اضافه شود را نیز برای انجام عملیات افزودن به آن پاس میدهیم.
const handleAddUser = (user) => { dispatch({ type: 'addUser', newUser: user }) }
به طور مشابه، میتوانیم handleUpdateUser
و handleDeleteUser
را نیز پیادهسازی کنیم.
const handleUpdateUser = (updatedUser) => { dispatch({ type: 'updateUser', updatedUser: updatedUser }) } const handleDeleteUser = (userId) => { dispatch({ type: 'deleteUser', id: userId }) }
newUser
، updatedUser
و userId
پارامترهایی هستند که از کامپوننتهای AddUserForm
و UsersList
منتقل میشوند. آنها حاوی دادههای مورد نیاز برای بهروزرسانی state میباشند.
برای هر featureای که در برنامه React خود ایجاد میکنیم، بهروزرسانیهای state بخش مهمی از پیادهسازی آن را تشکیل میدهند. با افزایش پیچیدگی برنامه، تعداد بهروزرسانیهای state نیز افزایش مییابد.
در این مقاله بررسی کردیم که reducer چیست و چرا به آن نیاز داریم. همچنین به کمک یک مثال دیدیم که اگر برای بهروزرسانیهای state یک تابع جداگانه در یک قسمت جدا داشته باشیم چقدر این عمل راحتتر انجام میشود. همینطور این کار باعث میشود تا کدی که داریم خواناتر بوده و قابلیت دسترسی بیشتری داشته باشد.
دیدگاهها: