مقدمه

به فصل دهم خوش آمدید! تاکنون، هوک useState ابزار اصلی ما برای مدیریت وضعیت در کامپوننت‌ها بوده است. useState برای stateهای ساده (مانند یک عدد، رشته یا مقدار بولی) و حتی برای اشیاء و آرایه‌های نه چندان پیچیده، فوق‌العاده است. اما با رشد اپلیکیشن، گاهی اوقات منطق به‌روزرسانی state بسیار پیچیده می‌شود. برای مثال، ممکن است چندین عمل مختلف وجود داشته باشد که همگی یک شیء state را به روش‌های متفاوتی تغییر می‌دهند.

در این سناریوها، قرار دادن تمام منطق به‌روزرسانی در داخل event handler ها می‌تواند کامپوننت را شلوغ و درک آن را دشوار کند. برای حل این مشکل، React یک هوک جایگزین به نام useReducer ارائه می‌دهد. useReducer به ما اجازه می‌دهد تا منطق به‌روزرسانی state را از کامپوننت جدا کرده و آن را در یک تابع مجزا به نام «کاهنده» یا reducer متمرکز کنیم.

useReducer چیست؟

هوک useReducer یک جایگزین برای useState است که از الگوی مدیریت وضعیت محبوب در کتابخانه‌هایی مانند Redux الهام گرفته شده است. این هوک زمانی مفید است که شما یک state پیچیده با چندین انتقال وضعیت مختلف دارید.

ساختار useReducer

useReducer دو آرگومان اصلی می‌گیرد و یک آرایه با دو عضو برمی‌گرداند:

  • تابع reducer: تابعی که منطق به‌روزرسانی state را در خود دارد. این تابع state فعلی و یک «اکشن» (action) را دریافت کرده و state بعدی را برمی‌گرداند.
  • state اولیه: مقدار اولیه برای state.

خروجی آن نیز شامل state فعلی و یک تابع dispatch است.

Copy Icon JAVASCRIPT (React)
const [state, dispatch] = useReducer(reducer, initialState);

تابع dispatch تنها راه برای به‌روزرسانی state است. شما یک «اکشن» را به این تابع dispatch می‌کنید، و React state فعلی و آن اکشن را به تابع reducer شما پاس می‌دهد. reducer شما state بعدی را محاسبه کرده و برمی‌گرداند و React کامپوننت را با state جدید دوباره رندر می‌کند.

مثال کاربردی: یک شمارنده پیچیده

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

۱. تعریف reducer

ابتدا، تابع reducer را تعریف می‌کنیم. این تابع معمولاً خارج از کامپوننت تعریف می‌شود.

Copy Icon JAVASCRIPT (React)
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return { count: 0 };
    default:
      throw new Error();
  }
}

تابع reducer ما یک شیء state و یک شیء action را می‌گیرد. شیء action معمولاً یک پراپرتی type دارد که نوع به‌روزرسانی را مشخص می‌کند. بر اساس این type، ما state جدید را محاسبه کرده و برمی‌گردانیم.

۲. استفاده از useReducer در کامپوننت

حالا از این reducer در کامپوننت خود استفاده می‌کنیم.

Copy Icon JAVASCRIPT (React)
import { useReducer } from 'react';

// ... reducer function defined above ...

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </>
  );
}

در این کامپوننت، ما useReducer را با reducer خود و یک state اولیه فراخوانی می‌کنیم. سپس در event handler های دکمه‌ها، تابع dispatch را با اکشن‌های مختلف فراخوانی می‌کنیم. این کار باعث می‌شود که منطق به‌روزرسانی state به صورت کاملاً تمیز و جدا از UI کامپوننت مدیریت شود.

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

useReducer به خصوص در اپلیکیشن‌های بزرگ که در آنها state بعدی به state قبلی بستگی دارد و چندین نوع به‌روزرسانی وجود دارد، بسیار مفید است. در درس بعدی، به «مدیریت وضعیت پیچیده با reducer» با جزئیات بیشتری خواهیم پرداخت و نحوه ارسال داده‌های اضافی (payload) به reducer را بررسی خواهیم کرد.