یکپارچه سازی React با کتابخانه‌های دیگر – بخش ۱

از React می‌توان در هر برنامۀ وب استفاده کرد. می‌تواند در برنامه‌های دیگر قرار گیرد و با کمی دقت، برنامه‌های دیگر هم می‌توانند در React قرار بگیرند. این بخش برخی از موارد کاربرد رایج را با تمرکز بر یکپارچه سازی با jQuery و Backbone آزمایش می‌کند، اما از همین ایده‌ها می‌توان در یکپارچه سازی کامپوننت‌ها با کدهای موجود استفاده کرد.

یکپارچه سازی با پلاگین‌های دستکاری DOM

ری‌اکت متوجه تغییراتی که در DOM خارج از آن رخ داده، نمی‌شود. ری‌اکت آپدیت‌ها را بر اساس اطلاعات داخل خودش تعیین می‌کند و اگر DOM node یکسانی توسط کتابخانۀ دیگری دستکاری شود، React گیج شده و راهی برای بهبودی ندارد.

البته این به این معنا نیست که ترکیب React با راه‌های دیگر اثرگذاری بر DOM غیرممکن یا حتی لزوما سخت است. شما فقط باید حواستان به این که هر چیزی چه کاری انجام می‌دهد باشد.

آسان‌ترین راه اجتناب از تعارض‌ها، جلوگیری از آپدیت شدن کامپوننت‌های React است. شما می‌توانی این کار را با رندر کردن المان‌هایی که React دلیلی برای آپدیت کردن آنها ندارد انجام دهید، مانند یک <div /> خالی.

چگونه مشکل را حل کنیم

برای نشان دادن این مطلب، بیایید یک طرح بسته بندی شده را برای پلاگین کلی jQuery پیاده کنیم.

ما یک ref را به المان root DOM می‌چسبانیم. در داخل componentDidMount یک مرجع به آن خواهیم یافت پس می‌توانیم آن را به پلاگین jQuery منتقل کنیم.

برای جلوگیری React از متاثر شدن از DOM پس از نصب، ما یک <div /> خالی از متد render() برگشت می‌دهیم. المان <div /> هیچ مشخصه یا childی ندارد، در نتیجه React هیچ دلیلی برای آپدیت آن ندارد، لذا پلاگین jQuery آزاد است تا آن قسمت از DOM را کنترل کند:

class SomePlugin extends React.Component {
  componentDidMount() {
    this.$el = $(this.el);
    this.$el.somePlugin();
  }

  componentWillUnmount() {
    this.$el.somePlugin('destroy');
  }

  render() {
    return <div ref={el => this.el = el} />;
  }
}

دقت کنید که ما هر دوی componentDidMount و componentWillUnmount را تعریف کرده‌ایم. خیلی از پلاگین‌های jQuery شنونده‌های رویدادها را به DOM می‌چسبانند، لذا مهم است که آنها را از componentWillUnmount جدا (detach) کنیم. اگر پلاگین روشی برای پاک کردن فراهم نکرده است، احتمالا شما مجبور به فراهم کردن آن خواهید بود. به یاد داشته باشید تمام شنونده‌های رویدادها را که پلاگین برای جلوگیری از نشت‌های حافظه ثبت کرده، حذف کنید.

یکپارچه سازی با پلاگین انتخاب شدۀ jQuery

به عنوان یک مثال بنیادی دیگر در رابطه با این موضوع، بیایید یک بسته بندی کنندۀ کوچک برای پلاگین انتخاب شده (Chosen) که ورودی‌های <select> را تکمیل می‌کند بنویسیم.

نکته: فقط چون ممکن است، این معنی را نمی‌دهد که بهترین راه حل برای برنامه‌های React باشد. ما شما را ترغیب به استفاده از کامپوننت‌های React، وقتی که می‌توانید، می‌کنیم. استفادۀ مجدد از کامپوننت‌های React در برنامه‌های React آسان‌تر است و اغلب کنترل بیشتری روی رفتار و ظاهرشان برایتان فراهم می‌کنند.

در ابتدا بیایید ببینیم Chosen با DOM چه کار می‌کند.

اگر شما آن را روی <select> DOM node فراخوانی کنید، attribute های DOM node اصلی را می‌خواند، با سبکی درونی پنهانش می‌کند، و سپس یک DOM node مجزا با نمایش بصری درست بعد از <select> الحاق می‌کند. در ادامه رویدادهای jQuery را برای آگاه کردن ما از تغییرات برمی‌افروزد.

بیایید بگوییم که این همان APIیی است که ما با بسته بندی کنندۀ کامپوننت ری‌اکت <Chosen>  در تلاش برای رسیدن به آن هستیم:

function Example() {
  return (
    <Chosen onChange={value => console.log(value)}>
      <option>vanilla</option>
      <option>chocolate</option>
      <option>strawberry</option>
    </Chosen>
  );
}

ما جهت سادگی آن را به صورت یک کامپوننت کنترل نشده پیاده سازی می‌کنیم.

ابتدا ما یک کامپوننت خالی با متد render() در جایی که <select> بسته بندی شده را در یک <div> برمی‌گردانیم، خواهیم ساخت:

class Chosen extends React.Component {
  render() {
    return (
      <div>
        <select className="Chosen-select" ref={el => this.el = el}>
          {this.props.children}
        </select>
      </div>
    );
  }
}

دقت داشته باشید که ما چگونه <select> را داخل یک <div> بسته بندی کردیم. این لازم است چون Chosen یک المان DOM دیگر را درست بعد از <select> node که ما منتقل کردیم الحاق می‌کند. با این حال تا زمانی که React پیگیر است، همیشه <div> فقط یک child خواهد داشت. این همان روشی است که ما مطمئن می‌شویم آپدیت‌های React تعارضی با DOM node الحاقی توسط Chosen ندارد. این نیز مهم است که اگر شما DOM را خارج از جریان React ویرایش کنید، باید مطمئن شوید که React دلیل برای دست زدن به آن DOM node ها ندارد.

در ادامه، ما چرخۀ زندگی را پیاده سازی می‌کنیم. ما نیاز به راه اندازی Chosen با ارجاع به <select> node در componentDidMount و برچیدن آن در componentWillUnmount داریم.:

componentDidMount() {
  this.$el = $(this.el);
  this.$el.chosen();
}

componentWillUnmount() {
  this.$el.chosen('destroy');
}

می‌توانید در CodePen آن را امتحان کنید.

به یاد داشته باشید که React هیچ معنی خاصی را به this.el اختصاص نمی‌دهد. آن فقط به خاطر این که ما قبلا آن فیلد را از یک ref در داخل متد render() اختصاص دادیم، کار می‌کند:

<select className="Chosen-select" ref={el => this.el = el}>

این برای رندر کامپوننت ما کافی است، اما ما می‌خواهیم دربارۀ تغییرات مقادیر نیز بدانیم. برای انجام این کار، ما در رویداد change مربوط به jQuery روی <select> کنترل شده توسط Chosen شریک خواهیم شد.

ما this.props.onChange را مستقیما به Chosen منتقل نمی‌کنیم، زیرا prop های کامپوننت ممکن است در طول زمان تغیر کنند و آن شامل کنترل کننده‌های رویدادها می‌شود. در مقابل، ما یک متد handleChange() تعریف می‌کنیم که this.props.onChange را فراخوانی کرده و آن را در رویداد change مربوط به jQuery شریک می‌کند.

componentDidMount() {
  this.$el = $(this.el);
  this.$el.chosen();

  this.handleChange = this.handleChange.bind(this);
  this.$el.on('change', this.handleChange);
}

componentWillUnmount() {
  this.$el.off('change', this.handleChange);
  this.$el.chosen('destroy');
}

handleChange(e) {
  this.props.onChange(e.target.value);
}

می‌توانید در CodePen آن را امتحان کنید.

در نهایت، فقط یک چیز برای انجام دادن باقی می‌ماند. در ری‌اکت، prop ها می‌توانند در طول زمان تغییر کنند. برای مثال، کامپوننت <Chosen> اگر حالت کامپوننت parent تغییر کند، می‌تواند child های متفاوتی داشته باشد. این به آن معنی است که در نقاط ادغام مهم است که ما به طور دستی DOM را در پاسخ به آپدیت‌های prop آپدیت کنیم، چون دیگر به React اجازه نمی‌دهیم DOM را برایمان مدیریت کند.

مستندات Chosen پیشنهاد می‌کنند که ما از jQuery trigger() API برای آگاهی در مورد تغییرات المان DOM اصلی استفاده کنیم. ما به React اجازه خواهیم داد بر آپدیت this.props.children در داخل <select> مدیریت کند، اما یک چرخۀ زندگی componentDidUpdate() را اضافه خواهیم کرد که Chosen را از تغییرات داخل لیست child ها آگاه خواهد کرد:

componentDidUpdate(prevProps) {
  if (prevProps.children !== this.props.children) {
    this.$el.trigger("chosen:updated");
  }
}

با این راه، Chosen خواهد فهمید که المان‌های DOM را هنگامی که child های <select> مدیریت شده توسط React تغییر کردند، آپدیت کند.

پیاده سازی کامل کامپوننت Chosen این گونه خواهد بود:

class Chosen extends React.Component {
  componentDidMount() {
    this.$el = $(this.el);
    this.$el.chosen();

    this.handleChange = this.handleChange.bind(this);
    this.$el.on('change', this.handleChange);
  }
  
  componentDidUpdate(prevProps) {
    if (prevProps.children !== this.props.children) {
      this.$el.trigger("chosen:updated");
    }
  }

  componentWillUnmount() {
    this.$el.off('change', this.handleChange);
    this.$el.chosen('destroy');
  }
  
  handleChange(e) {
    this.props.onChange(e.target.value);
  }

  render() {
    return (
      <div>
        <select className="Chosen-select" ref={el => this.el = el}>
          {this.props.children}
        </select>
      </div>
    );
  }
}

می‌توانید در CodePen آن را امتحان کنید.

** در مقاله بعدی به ادامه مبحث یکپارچه سازی ( Integrating with Other Libraries ) در React خواهیم پرداخت. مشاهده بخش ۲

1 Star2 Stars3 Stars4 Stars5 Stars (2 votes, average: 5٫00 out of 5)
Loading...
counter customizable free hit