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

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

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

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

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

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

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

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

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

می‌توانیم با استفاده از هوک 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 داشته باشد.

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

برای این کار یک مثال ساده که در آن لیستی از کاربران داریم را باهم بررسی می‌کنیم. در این مثال می‌توانیم یک کاربر جدید اضافه کنیم، یک کاربر موجود را حذف کنیم و یا این که جزئیات مربوط به یک کاربر را به‌روزرسانی کنیم. معمولا برای انجام این کار یک متغیر 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 را تعریف کنیم؟

متد 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 یک تابع جداگانه در یک قسمت جدا داشته باشیم چقدر این عمل راحت‌تر انجام می‌شود. همینطور این کار باعث می‌شود تا کدی که داریم خواناتر بوده و قابلیت دسترسی بیشتری داشته باشد.

دیدگاه‌ها:

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