در تفکر کامپوننت، بویژه کامپوننت های فانکشنال، کامپوننت سطح بالاتر یا Higher Order Component جایگاه بسیار ویژه ای دارد. اگر از توسعه دهندگان قدیمی واسط کاربری هستید یادتان هست که در گذشته اغلب فریمورک ها از مفهوم وراثت برای ساخت UI استفاده می‌کردند. بدین معنی که یک کلاس پایه، مثلا TextBox تعریف شده بود و معمولا شما به فراخور نیاز کلاسی می ساختید تا از آن ارث بگیرد و کارکردهای جدید به آن اضافه می شد. این شیوه مبتنی بر اولویت وراثت بر ترکیب بود. اما امروزه این شیوه معکوس شده است. با تغییر رویکرد، مفهوم استفاده مجدد دستخوش تغییراتی شده است. یکی از راه های استفاده مجدد در رویکرد اولویت ترکیب بر وراثت، بهره گیری از کامپوننت سطح بالاتر است که در این نوشتار شیوه ساخت این کامپوننت در فریمورک انگولار را بررسی میکنیم.


ویدیو در یوتیوب

ویدیو در آپارات

سناریویی رو در نظر بگیرید که در آن یک لاجیک مشخص دارید و در کامپوننت های زیادی تکرار می‌شود. مثلا فرض کنید در کامپوننت از ابزرور های مختلفی استفاده می‌کنید و در هوک OnDestroy میخواهید عضویت خود را لغو نمایید. تا از مموری لیک جلوگیری کنید. تکه کد زیر را در کامپونتت ببنید

private sink:Subscription =  new Subscription();
ngOnInit(): void {
    this.sink.add(this.service.subscribe((data)=>console.log))
}
ngOnDestroy(): void {
       this.sb.unsubscribe()
}

این کد بویلرپلت به نظر میاد و اگر فراموش شود همانطور که گفته شد با هر بار حذف و اضافه شدن کامپوننت به دام یک عضویت جدید ساخته می شود که از حافظه هم پاک نمی‌شود. طبعا وراثت مناسب این کار نیست چون مفهوم اساسا وراثت نیست. Mixin میتواند گزینه دیگری باشد ولی به نظرم برای این سناریو دور از ماهیت فانکشنال و تفکر کامپوننتی است. در چنین حالت هایی بهره گیری از توایع سطح بالاتر بسیار سودمند است. یعنی تابعی که کامپوننتی دریافت کند و پس از تغییرات کامپوننت جدیدی برگرداند. با این کار بسیاری از مسایل Cross Cutting را نیز پیاده نمود. از آنجا که تایپ اسکریپت از مفهوم دکوراتور پشتیبانی میکند -دراین پست در مورد دکوراتور تایپ اسکریپت به تفصیل صحبت شده است – و همچنین انگولار از نسخه ۸ به بعد از پایپ لاین رندرینگ Ivy بهره میگیرد می‌توان کدهای تمیزی برای این منظور نوشت

کامپوننت سطح بالاتر به کمک Ivy

یکی از ويژگی های جذاب Ivy قابلیتی است که به سبب آن کامپوننت میتواند خودش را رندر کنید به تعبیری هرچیزی که برای رندر کامپوننت لازم است در پروسه کامپایل به کامپوننت اضافه می شود و دیگر نیازی تمپلت انجین یا چیزی شبیه آن ندارد.مزیت دیگر امکان تعریف کامپوننت سطح بالاتر است که در ادامه یک نمونه از آن را خواهیم ساخت.

فرض کنید میخواهیم دکوراتوری ایجاد کنیم که هوک چرخه حیات، برای از بین رفتن کامپوننت، یعنی OnDestroy را به گونه ای تغییر دهد تا در صورتی که کلن وجود ندارد به آن اضافه شود و ابزورها را لغو کند و اگر وجود دارد آن را اجرا و منظق مورد نظر را هم اضافه کند. دکوراتور آن به شرح زیر خواهد بود.

export function autoUnsubscribe(field = 'sink') {

  return (target) => {
    const originalFactory = target.ɵfac;

    target.ɵfac = (...args) => {
      const cmp = originalFactory(...args);

      const originDestroy = target.prototype.ngOnDestroy;
      target.prototype.ngOnDestroy = function ngOnDestroy() {
        if (originDestroy) {
          originDestroy.apply(this, ...args);
        }
        (this[field] as Subscription)?.unsubscribe();
      };
      return cmp;
    };

    return target;
  };

}

این دکوراتور اسم متغیر عضویت ها را از کاربر میگیرد دکوراتوری را برمیگرداند که فاکتوری کامپوننت – که به واسطه Ivy اضافه شده است را تغییر می دهد و مکانیزم OnDestroy را اصلاح میکند

شیوه استفاده هم اینگونه خواهد بود.

@autoUnsubscribe()
@Component({
  selector: 'app-test',
})
export class TestComponent

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *