مفهوم Reducer در جاوااسکریپت
عباس سپهوند
عباس سپهوند
  • 1401/07/02
  • 15 دقیقه
  • 0 نظر

مفهوم Reducer در جاوااسکریپت

Reducer یکی از مفاهیم جاوااسکریپت است که به دلیل استفاده آن در کتابخانه ری اکت بی اندازه مورد توجه قرار گرفته است. در این مقاله به معرفی این مفهوم در جاوااسکریپت می پردازیم

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

Reducer در حقیقت تابعی است که دو آرگومان به عنوان ورودی دریافت می کند (state جاری و یک action) و بر مبنای این دو یک state جدید بر می گرداند. ساختار این تابع به شکل زیر است:

(state, action) => newState

به عنوان مثال، در سناریوی زیر می خواهیم یک عدد را یک واحد افزایش دهیم:

function counterReducer(state, action) {
  return state + 1;
}

 

و اگر بخواهیم به صورت arrow function آن را بیان کنیم داریم:

const counterReducer = (state, action) => {
  return state + 1;
};

 

در مثال فوق state جاری یک عدد است و reducer function این عدد را یک واحد افزایش می دهد. چنانچه state را به count  تغییر نام دهیم شاید برای توسعه دهندگان تازه کار واضح تر باشد. ولی در هر حالتی باید به خاطر داشته باشید که count همچنان یک state است:

const counterReducer = (count, action) => {
  return count + 1;
};

 

Reducer function تابعی است بدون هیچ site-effect ی و این یعنی همیشه و در همه حال تعداد آرگومان های ورودی (state  و action) و خروجی (new state) یکسان است.

آرگومان دوم که action نام دارد در حقیقت آبجکتی است که خصوصیتی به نام type دارد. Reducer  بر مبنای این خصوصیت، تصمیم می گیرد چه state ی باید به عنوان state  جدید برگردانده شود:

const counterReducer = (count, action) => {
  if (action.type === 'INCREASE') {
    return count + 1;
  }

  if (action.type === 'DECREASE') {
    return count - 1;
  }

  return count;
};

 

اگر برای خصوصیت type هیچ مقداری تعیین نشود یا برای مقدار ارسالی هیچ منطقی پیاده سازی نشود، state جاری بدون تغییر به عنوان خروجی تابع برگردانده می شود. معمولا برنامه نویسان ترجیح می دهند به جای استفاده از if-else  از switch case  در پیاده سازی بدنه reducer function  استفاده کنند. هر چند هیچ الزامی وجود ندارد ولی به عنوان یک قاعده کلی می توانید از آن تبعیت کنید. در کد زیر همان منطق قبل اما با switch case  پیاده سازی شده است:

const counterReducer = (count, action) => {
  switch (action.type) {
    case 'INCREASE':
      return count + 1;
    case 'DECREASE':
      return count - 1;
    default:
      return count;
  }
};

 

دقت کنید در این مثال count را به عنوان state در نظر گرفته ایم ولی در برنامه های واقعی اغلب، state شما یک آبجکت است که می تواند پیچیده هم باشد. به عنوان مثال count می تواند یک خصوصیت از state object شما باشد:

const counterReducer = (state, action) => {
  switch (action.type) {
    case 'INCREASE':
      return { ...state, count: state.count + 1 };
    case 'DECREASE':
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
};

 

در کد فوق دو نکته مهم وجود دارد که به ترتیب آن ها را بررسی می کنیم:

  • Stateی که توسط reducer function پردازش می شود باید غیر قابل تغییر یا immutable  باشد

    تغییر ناپذیر بودن یا immutability به این معنی است که state  ِ ورودی (که به شکل آرگومان به reducer ارسال می شود)، مستقیما قابل تغییر نیست. در نتیجه reducer function باید همیشه یک state object ِ جدید برگرداند.

  • برای تضمین immutability ما از JavaScript spread operator استفاده می کنیم. این اپراتور، یک آبجکت جدید از state ِ ورودی و بخشی که تغییر داده ایم (خصوصیت count) ایجاد می کند. با این کار ما تضمین می کنیم که دیگر خصوصیات آبجکت state، بدون تغییر باقی می مانند و فقط خصوصیتی که تغییر کرده است به همراه سایر خصوصیات به عنوان یک state ِ جدید برگردانده می شود.

 

اجازه بدهید این دو نکته مهم را در مثال زیر بیشتر بررسی کنیم. در مثال زیر می خواهیم نام خانوادگی شخصی را که در آبجکتِ person قرار دارد تغییر دهیم. دقت کنید person  را به عنوان state در نظر می گیریم:

const personReducer = (person, action) => {
  switch (action.type) {
    case 'INCREASE_AGE':
      return { ...person, age: person.age + 1 };
    case 'CHANGE_LASTNAME':
      return { ...person, lastname: action.lastname };
    default:
      return person;
  }
};

 

نحوه فراخوانی و استفاده از reducer function به صورت زیر است:

const initialState = {
  firstname: 'Liesa',
  lastname: 'Huppertz',
  age: 30,
};

const action = {
  type: 'CHANGE_LASTNAME',
  lastname: 'Wieruch',
};

const result = personReducer(initialState, action);

expect(result).to.equal({
  firstname: 'Liesa',
  lastname: 'Wieruch',
  age: 30,
});

 

همانطور که می بینید با استفاده از spread operator همه خصوصیات state ِ جاری را برای state ِ جدید در نظر گرفته ایم اما خصوصیت lastname  را تغییر داده ایم. به همین دلیل است که یکی از مهمترین کاربردهای spread operator ، پیاده سازی مفهوم immutability است.  یعنی عدم تغییر state به شکل مستقیم.

آبجکت ِ action  غیر از خصوصیت type، خصوصیتی دیگری به نام payload  می تواند داشته باشد که البته این خصوصیت اختیاری است. payload در حقیقت زمانی استفاده می شود که می خواهیم اطلاعات بیشتری به reducer برای تغییر state ارسال کنیم. در همین مثال، reducer، در مورد نام خانوادگی جدید هیچ اطلاعاتی ندارد. ما باید نام خانوادگی جدید را از طریق payload ارسال کنیم. به عبارت دقیق تر action object ِ ما باید به شکل زیر تغییر کند:

const action = {
  type: 'CHANGE_LASTNAME',
  payload: {
    lastname: 'Wieruch',
  },
};

 

علاوه بر تغییر فوق لازم است reducer function را به شکل زیر تغییر دهیم:

const personReducer = (person, action) => {
  switch (action.type) {
    case 'INCREASE_AGE':
      return { ...person, age: person.age + 1 };
    case 'CHANGE_LASTNAME':
      return { ...person, lastname: action.payload.lastname };
    default:
      return person;
  }
};

 

به طور خلاصه هدف کلی reducer ها، تغییر state از وضعیت A به وضعیت B است که این کار به کمک action ها و از طریق دو خصوصیت type و payload انجام می شود. reducer تابعی است که دو آرگومان به نام های state و action از ورودی دریافت می کند و یک state جدید را به عنوان خروجی بر می گرداند. مهمترین خصوصیت reducer function ها تضمین immutability است. به این معنی که state نباید به شکل مستقیم تغییر کند. در عوض تغییرات روی state به شکل غیر مستقیم و با برگرداندن یک state  جدید باید انجام شود. اینکار را می توان با استفاده از spread operator به سادگی انجام داد. علاوه بر موارد فوق، reducer function، به یک action object نیاز دارد که این آبجکت یک خصوصیت الزامی به نام type و یک خصوصیت اختیاری به نام payload دارد. از خصوصیت type به منظور پیاده سازی منطق های مختلف استفاده می شود که اغلب از switch case استفاده می شود و از خصوصیت payload برای ارسال اطلاعات بیشتر به reducer function استفاده می کنیم.

در پایان در نظر داشته باشید، reducer یکی از مفاهیم پر کاربرد در React است.  useReducer پیاده سازی reducer در کتابخانه ری اکت است. توصیه می کنم چنانچه به این کتابخانه علاقه مند هستید، آموزش ری اکت را در وب سایت کلاسبن دنبال کنید.

دیدگاه

برای ارسال دیدگاه های خود ابتدا وارد شوید یا ثبت نام کنید

ورود یا ثبت نام
عباس سپهوند
عباس سپهوند

برنامه نویس و توسعه دهنده نرم افزار

مشاهده پروفایل
5 مقاله اخیر

آموزش React: راهنمای کامل useCallback

عباس سپهوند
زمان مطالعه: 40

آموزش React: راهنمای کامل useEffect

عباس سپهوند
زمان مطالعه: 25

آموزش React: راهنمای کامل Ref ها در React

عباس سپهوند
زمان مطالعه: 15
مشاهده همه مقالات