Higher Order Component یا HOC چیست؟

?What is the Higher Order Component

23 بهمن 1399
Higher Order Component یا HOC چیست؟

آشنایی با کامپوننت های Higher Order یا HOC در ری اکت

در جلسه قبل با HOC در ری اکت تا حدی آشنا شدیم (از Aux استفاده کردیم). کار HOC ها این است که یک کامپوننت دیگر را درون خودشان قرار دهند. HOC ها منطق جداگانه خودشان را ندارند، استایل دهی خاص خود را ندارند و هیچ ساختاری به کدهای JSX یا DOM واقعی نمی دهند. نهایت کار آن ها این است که یک کامپوننت دیگر را درون خود قرار داده و نهایتا چیز بیشتری به آن اضافه کنند.

قبل از اینکه در جلسه قبل از Aux استفاده کنیم، از یک div استفاده می کردیم که کلاس خاصی را به آن داده بودیم و مسئولیت استایل دهی را بر عهده داشت. حالا که از Aux خالی استفاده می کنیم استایل دهی خاصی نداریم، بنابراین می خواهیم در این جلسه از یک HOC با استایل استفاده کنیم.

برای مثال یک HOC دیگر به نام WithClass.js ایجاد می کنیم (درون پوشه hoc). معمولا قرارداد نام گذاری HOC ها بدین شکل و با کلمه With است اما شما هر طور که خواستید می توانید این فایل ها را نام گذاری کنید. اولین کاری که در فایل WithClass انجام می دهیم وارد کردن react است چرا که می خواهیم در آن از JSX استفاده کنیم:

import React from 'react';

const withClass = props => (
    <div className={props.classes}>
        {props.children}
    </div>
);

export default withClass;

در کد بالا از حالت خلاصه Arrow function استفاده کرده ایم. همچنین props.classes خصوصیتی است که قرار است به HOC ما ارسال شود. شما می توانید به جای Classes هر نام دیگری را نیز قرار دهید. props.children نیز یعنی هر چیزی که بین تگ های آغازین و پایانی کامپوننت withClass قرار داشته باشد. کل کاری که این HOC انجام میدهد این است که یک کلاس برای یک div تعیین می کند و سپس آن div کامپوننت های دیگر را در بر خواهد گرفت.

برای تست کردن این کامپوننت به فایل App.js بروید و آن را import کنید:

import WitchClass from '../hoc/WitchClass';

حالا به قسمت JSX همین کدها می رویم و div را با witchClass جا به جا می کنیم. در حال حاضر کدهای این قسمت به شکل زیر هستند:

    return (
      <div className={classes.App}>
        <button
          onClick={() => { this.setState({ showCockpit: false }) }}
        >Remove Cockpit</button>
        {this.state.showCockpit ?
          <Cockpit
            title={this.props.appTitle}
            showPersons={this.state.showPersons}
            personsLength={this.state.persons.length}
            clicked={this.togglePersonsHandler} /> : null}
        {persons}
      </div>
    );

ما باید آن را بدین شکل تغییر دهیم:

    return (
      <WitchClass classes={classes.App}>
        <button
          onClick={() => { this.setState({ showCockpit: false }) }}
        >Remove Cockpit</button>
        {this.state.showCockpit ?
          <Cockpit
            title={this.props.appTitle}
            showPersons={this.state.showPersons}
            personsLength={this.state.persons.length}
            clicked={this.togglePersonsHandler} /> : null}
        {persons}
      </WitchClass>
    );

توجه کنید که className را به classes تغییر داده ایم چرا که آن را به صورت prop در کامپوننت WitchClass دریافت خواهیم کرد. حالا اگر به مرورگر برگردیم متوجه خواهیم شد که همه چیز مانند قبل کار می کند.

هیچ اشکالی ندارد که از div های ساده به جای HOC ها استفاده کنید اما در آینده خواهید دید که ما با استفاده از HOC ها از قابلیت Error handling و مدیریت خطاها استفاده خواهیم کرد تا کامپوننت هایی را که درخواست HTTP ایجاد میکنند (یا کار های شبیه به آن) در یک HOC قرار دهیم و خطاهای احتمالی آن ها را مدیریت کنیم.

روشی که بالاتر برای ساخت HOC ها استفاده کردیم تنها یکی از دو روش موجود ساخت HOC بود. روش دوم ساخت HOC ها نیازی به برگرداندن یک کامپوننت کاربردی ندارد، بلکه یک تابع ساده جاوا اسکریپتی است:

آرگومان اول این تابع عنصری است که قرار است درون کامپوننت ما قرار بگیرد. شما می توانید هر نامی برای آن انتخاب کنید اما حتما باید نام انتخابی با حرف بزرگ انگلیسی شروع شود چرا که اشاره به یک کامپوننت خواهد داشت. آرگومان دوم نیز  آن چیزی است که درون HOC خود بدان نیاز دارید بنابراین بستگی دارد که چه چیزی بخواهید. ما در اینجا می خواهیم از className استفاده کنیم. سپس درون این تابع یک کامپوننت کاربردی را برمی گردانم! می دانم که احتمالا برایتان خیلی عجیب است؛ ما یک تابع ساده جاوا اسکریپتی داریم سپس درون آن یک تابع دیگر از نوع کامپوننت کاربردی داریم که برگردانده می شود. مثال:

const withClass = (WrappedComponent, className) => {
    return props => (
        <div className={className}>
            <WrappedComponent />
        </div>
    );
}

دو آرگومان این تابع را می بینید: WrappedComponent که کامپوننتی است که درون این HOC قرار خواهد گرفت و سپس className که قرار است کلاس ما باشد. درون خود تابع یک تابع دیگر (کامپوننت کاربردی) قرار دارد که برای className مقدار className را می گیرد. توجه کنید که className استفاده شده همان className گرفته شده در آرگومان دوم تابع است.

حالا چطور باید از چنین HOC استفاده کرد؟ به فایل App.js بروید و کد زیر را تغییر دهید:

    return (
      <WitchClass classes={classes.App}>
        <button
          onClick={() => { this.setState({ showCockpit: false }) }}
        >Remove Cockpit</button>
        {this.state.showCockpit ?
          <Cockpit
            title={this.props.appTitle}
            showPersons={this.state.showPersons}
            personsLength={this.state.persons.length}
            clicked={this.togglePersonsHandler} /> : null}
        {persons}
      </WitchClass>
    );

از آنجایی که دیگر WitchClass وجود ندارد (آن را تبدیل به یک تابع ساده جاوا اسکریپتی کردیم) باید دوباره از Aux استفاده کنیم:

    return (
      <Aux>
        <button
          onClick={() => { this.setState({ showCockpit: false }) }}
        >Remove Cockpit</button>
        {this.state.showCockpit ?
          <Cockpit
            title={this.props.appTitle}
            showPersons={this.state.showPersons}
            personsLength={this.state.persons.length}
            clicked={this.togglePersonsHandler} /> : null}
        {persons}
      </Aux>
    );

واضح است که باید آن را در بالای صفحه import کنیم:

import witchClass from '../hoc/WitchClass';
import Aux from '../hoc/Auxiliary';

توجه داشته باشید که WitchClass را به withclass تبدیل کرده ام (کوچک کردن حرف w) چرا که دیگر یک کامپوننت نیست و تابعی ساده محسوب می شود (این کار اختیاری است). اگر شما هم withclass را با w کوچک نوشتید، نام فایل آن را نیز withClass.js بگذارید تا کاملا مشخص باشد که هیچ کامپوننت کاربردی در این فایل وجود ندارد.

حالا برای استفاده از آن باید به سراغ دستور export در انتهای فایل App.js برویم:

export default App;

این دستور باید به این شکل تغییر کند:

export default witchClass(App, classes.App);

اگر یادتان باشد پارامتر اول عنصری بود که قرار است درون HOC قرار بگیرد و پارامتر دوم نام کلاس بود. ما اینجا از CSS Module استفاده کرده ایم (classes.App). اگر از آنها چیزی یادتان نمی آید به جلسات مربوط به CSS Module مراجعه کنید. حالا اگر به مرورگر بروید همه چیز مانند قبل کار می کند و کدها صحیح و سالم است.

سوال: از کدام نوع از HOC ها استفاده کنیم؟

پاسخ: بستگی دارد! اگر HOC شما فقط قسمت هایی از HTML را تغییر می دهد و یا استایل دهی های خاصی را اضافه می کند از نوع اول (کامپوننت کاربردی) استفاده کنید و کد آن ها را درون کد JSX خود و به عنوان یک کامپوننت قرار دهید، اما اگر HOC شما منطق خاصی را به برنامه اضافه می کند (مثلا کدهای جاوا اسکریپتی که خطاها را مدیریت کرده و یا مسئول ارسال و جمع آوری analytics و آمار سایت است) بهتر است از نوع دوم استفاده شود (تابع جاوا اسکریپتی ساده). البته این موضوع فقط یک پیشنهاد است و شما در آخر می توانید از هر نوعی که خواستید برای هر حالتی که خواستید استفاده کنید.

امیدوارم به طور کامل با HOC در ری اکت آشنا شده باشید. در قسمت های بعدی به صورت عملی از آن ها استفاده خواهیم کرد و درک بهتری از آن ها پیدا می کنیم.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش ری اکت توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

محمد قاسم پور
23 دی 1400
قسمت آخر این درس، همه قیمه ها رو ریختی تو ماستا. آخه چرا امیر آقای گل؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.