استفاده از هوک useReducer در React

state بخش مهمی از یک برنامه React است و بیشتر قابلیت‌ها شامل به‌روزرسانی state در کامپوننت‌ها هستند. اما هر چه برنامه بزرگ‌تر می‌شود به‌‌روزرسانی‌های state نیز پیچیده‌تر می‌شوند. روش متفاوتی برای مدیریت به‌روزرسانی‌های state وجود دارد، و آن استفاده از reducer‌ها است. اما reducer‌ چیست؟ چطور باید از آن‌ها استفاده کنیم؟ هوک useReducer چه کاری در برنامه ما انجام می‌دهد؟

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

Reducer چیست و چرا به آن نیاز داریم؟

مثالی از یک برنامه To-Do را باهم بررسی می‌کنیم. این برنامه شامل افزودن، حذف و به‌روزرسانی موارد موجود در todo لیست است. خود عملیات به‌روزرسانی ممکن است شامل به‌روزرسانی و ایجاد تغییر در یک تسک و یا علامت‌گذاری آن به عنوان تسک تکمیل شده باشد.

هنگامی که ما یک todo لیست را پیاده‌سازی می‌کنیم، یک متغیر state به نام

todoList
todoListداریم که انجام هر عملیات و به‌روزرسانی‌های state را به کمک آن انجام خواهیم داد. با این حال، این به‌روزرسانی‌های state ممکن است در قسمت‌های مختلف ظاهر شوند، حتی گاهی اوقات این اتفاق در داخل کامپوننت صورت نمی‌گیرد.

برای اینکه کدی که داریم خوانایی بیشتری داشته باشد می‌توانیم تمام به‌روزرسانی‌های state را به یک تابع جدا منتقل کنیم که خارج از کامپوننت مورد نظر ما قرار دارد. بنابراین در حین انجام عملیات مورد نیاز، کامپوننت ما فقط باید یک متد را فراخوانی کرده و عملیاتی که می‌خواهیم انجام شود را انتخاب کند.

تابعی که شامل تمام به‌روزرسانی‌های state ما است، reducer نامیده می‌شود. این به این دلیل است که ما منطق state را به یک تابع جداگانه کاهش می‌دهیم. متدی که برای انجام عملیات فراخوانی می‌کنیم، متد

dispatch
dispatchاست.

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

می‌توانیم با استفاده از هوک useReducer یک reducer به کامپوننت خود اضافه کنیم. اکنون متد useReducer را از کتابخانه React به صورت زیر import می‌کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useReducer } from 'react'
import { useReducer } from 'react'
import { useReducer } from 'react'

متد useReducer یک متغیر state و یک متد

dispatch
dispatchبرای ایجاد تغییرات state به ما می‌دهد. state را می‌توانیم به صورت زیر تعریف کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const [state, dispatch] = useReducer(reducerMethod, initialValue)
const [state, dispatch] = useReducer(reducerMethod, initialValue)
const [state, dispatch] = useReducer(reducerMethod, initialValue)

متد reducer شامل منطق state است. ما می‌توانیم با استفاده از متد

dispatch
dispatch، منطق state را انتخاب کنیم. state همچنین می‌تواند مقداری اولیه مشابه با هوک useState داشته باشد.

مثال‌هایی از هوک useReducer

برای این کار یک مثال ساده که در آن لیستی از کاربران داریم را باهم بررسی می‌کنیم. در این مثال می‌توانیم یک کاربر جدید اضافه کنیم، یک کاربر موجود را حذف کنیم و یا این که جزئیات مربوط به یک کاربر را به‌روزرسانی کنیم. معمولا برای انجام این کار یک متغیر state به نام

user
userایجاد می‌کنیم و به‌روزرسانی‌های state در قسمت‌های مختلف را انجام می‌دهیم.

اکنون می‌خواهیم سعی کنیم همین کار را با استفاده از reducerها انجام دهیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const [users, dispatch] = useReducer(reducerMethod, userData);
const [users, dispatch] = useReducer(reducerMethod, userData);
const [users, dispatch] = useReducer(reducerMethod, userData);

برای این کار از داده‌های اولیه زیر استفاده می‌کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
},
]
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 }, ]
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 را تعریف کنیم؟

متد reducer شامل به‌روزرسانی‌های state است. این متد دو آرگومان می‌گیرد. آرگومان اول مقدار فعلی و آرگومان دوم یک آبجکت action است. آبجکت action شامل نوع action و داده‌های اضافی مورد نیاز برای انجام به‌روزرسانی می‌باشد.

ما سه نوع به‌روزرسانی انجام خواهیم داد: افزودن کاربر جدید، به‌روزرسانی کاربر و حذف آن. برای انتخاب نوع عملیاتی که باید انجام شود، از switch-case استفاده می‌کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const reducerMethod = (users, action) => {
switch(action.type) {
// State updates here
}
}
const reducerMethod = (users, action) => { switch(action.type) { // State updates here } }
const reducerMethod = (users, action) => {
    switch(action.type) {
        // State updates here
    }
}

فیلد

type
typeحاوی نام عملیاتی است که باید انجام شود. این یک رشته است و می‌توانیم هر مقداری را که می‌خواهیم تنظیم کنیم. فقط باید مطمئن شویم که برای خوانایی بهتر با action انجام شده مرتبط باشد. ابتدا عملیات افزودن را انجام می‌دهیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
case 'addUser': {
return [
...users,
action.newUser
]
}
case 'addUser': { return [ ...users, action.newUser ] }
case 'addUser': {
    return [
        ...users,
        action.newUser
    ]
}

منطق به‌روزرسانی state مشابه setState است. در این مثال به جای ایجاد تغییرات مستقیم در متغیر state، یک مقدار state جدید return می‌کنیم.

اکنون عملیات به‌روزرسانی را انجام می‌دهیم. در حین انجام عملیات به‌روزرسانی، متد dispatch یک آبجکت

updatedUser
updatedUserرا برای به‌روزرسانی کاربر موجود ارسال می‌کند. این داده‌های اضافی از طریق آبجکت
action
actionارسال می‌شود.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
case 'updateUser': {
return users.map(user => {
if(user.id == action.updatedUser.id)
return action.updatedUser
return user;
})
}
case 'updateUser': { return users.map(user => { if(user.id == action.updatedUser.id) return action.updatedUser return user; }) }
case 'updateUser': {
    return users.map(user => {
        if(user.id == action.updatedUser.id)
        	return action.updatedUser
        return user;
    })
}

در این مرحله برای عملیات حذف، متد

dispatch
dispatchفقط
id
idآبجکت را ارسال می‌کند تا آرایه state بتواند آن را فیلتر کند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
case 'deleteUser': {
return users.filter(user => user.id !== action.id)
}
case 'deleteUser': { return users.filter(user => user.id !== action.id) }
case 'deleteUser': {
  return users.filter(user => user.id !== action.id)
}

اگر بخواهیم یک مورد پیش‌فرض برای عملی غیر از موارد مشخص‌شده داشته باشیم، به صورت زیر عمل می‌کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
default: {
// Handle error here
}
default: { // Handle error here }
default: {
  // Handle error here
}

در ادامه قصد داریم تا کامپوننت‌هایی که در واقع از این reducer استفاده می‌کنند را بسازیم.

لیستی از کاربران با ویژگی‌های زیر را در کامپوننت

UserDetails
UserDetailsنمایش می‌دهیم‌:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<UsersList users={users}
handleUpdateUser={handleUpdateUser}
handleDeleteUser={handleDeleteUser}
/>
<UsersList users={users} handleUpdateUser={handleUpdateUser} handleDeleteUser={handleDeleteUser} />
<UsersList users={users}
           handleUpdateUser={handleUpdateUser}
           handleDeleteUser={handleDeleteUser}
 />

همچنین یک فرم برای افزودن کاربران جدید در کامپوننت

AddUserForm
AddUserFormایجاد می‌کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<AddUserForm handleAddUser={handleAddUser} />
<AddUserForm handleAddUser={handleAddUser} />
<AddUserForm handleAddUser={handleAddUser} />

ما در در این مثال به پیاده‌سازی واقعی کامپوننت‌ها اشاره نکرده‌ایم، زیرا تمرکز اصلی فقط بر روی قسمت به‌روزرسانی state است.

به‌روزرسانی‌های state را با فراخوانی متد

dispatch
dispatchو ارسال type به‌روزرسانی state با مقداری داده، در متدهای handler انجام می‌دهیم. همینطور کاربر جدیدی که قرار است اضافه شود را نیز برای انجام عملیات افزودن به آن پاس می‌دهیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const handleAddUser = (user) => {
dispatch({
type: 'addUser',
newUser: user
})
}
const handleAddUser = (user) => { dispatch({ type: 'addUser', newUser: user }) }
const handleAddUser = (user) => {
    dispatch({
        type: 'addUser',
        newUser: user
    })
}

به طور مشابه، می‌توانیم

handleUpdateUser
handleUpdateUserو
handleDeleteUser
handleDeleteUserرا نیز پیاده‌سازی کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const handleUpdateUser = (updatedUser) => {
dispatch({
type: 'updateUser',
updatedUser: updatedUser
})
}
const handleDeleteUser = (userId) => {
dispatch({
type: 'deleteUser',
id: userId
})
}
const handleUpdateUser = (updatedUser) => { dispatch({ type: 'updateUser', updatedUser: updatedUser }) } const handleDeleteUser = (userId) => { dispatch({ type: 'deleteUser', id: userId }) }
const handleUpdateUser = (updatedUser) => {
    dispatch({
        type: 'updateUser',
        updatedUser: updatedUser
    })
}

const handleDeleteUser = (userId) => {
    dispatch({
        type: 'deleteUser',
        id: userId
    })
}

newUser
newUser،
updatedUser
updatedUserو
userId
userIdپارامترهایی هستند که از کامپوننت‌های
AddUserForm
AddUserFormو
UsersList
UsersListمنتقل می‌شوند. آن‌ها حاوی داده‌های مورد نیاز برای به‌روزرسانی state می‌باشند.

جمع‌بندی

برای هر featureای که در برنامه React خود ایجاد می‌کنیم، به‌روزرسانی‌های state بخش مهمی از پیاده‌سازی آن را تشکیل می‌دهند. با افزایش پیچیدگی برنامه، تعداد به‌روزرسانی‌های state نیز افزایش می‌یابد.

در این مقاله بررسی کردیم که reducer چیست و چرا به آن نیاز داریم. همچنین به کمک یک مثال دیدیم که اگر برای به‌روزرسانی‌های state یک تابع جداگانه در یک قسمت جدا داشته باشیم چقدر این عمل راحت‌تر انجام می‌شود. همینطور این کار باعث می‌شود تا کدی که داریم خواناتر بوده و قابلیت دسترسی بیشتری داشته باشد.

دیدگاه‌ها:

افزودن دیدگاه جدید