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

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

یکپارچه سازی با View Library های دیگر

به لطف انعطاف پذیری ReactDOM.render()،ری‌اکت می‌تواند در هر برنامه‌ای قرار گیرد.

اگرچه معمولا از ری اکت در استارتآپ برای بارگذاری یک کامپوننت root ری‌اکت در داخل DOM استفاده می‌شود، ReactDOM.render() همچنین می‌تواند چندین دفعه برای قسمت‌های مستقلی از رابط کاربری، که می‌توانند به کوچکی یک دکمه و یا به بزرگی یک برنامه باشند، فراخوانی شود.

در حقیقت، این دقیقا همان روشی است که از React در Facebook استفاده می‌شود. این به ما اجازه می‌دهد برنامه‌ها را در React قطعه به قطعه بنویسیم و سپس آنها را با الگوهای تولید شدۀ سرور و کدهای سمت کاربر (client-side) با هم ترکیب کنیم.

جایگزینی رندر بر اساس رشته با ری‌اکت

یک الگوی رایج در برنامه‌های وب قدیمی‌تر، تعریف تکه‌های DOM به عنوان یک رشته و قرار دادن آن در DOM به صورت $el.html(htmlString) بود. این نکات در کدبیس برای معرفی React عالی است. فقط کافی است رندر بر اساس رشته را به صورت یک کامپوننت React مجددا بنویسید.

بنابراین پیاده سازی jQuery زیر ….

$('#container').html('<button id="btn">Say Hello</button>');
$('#btn').click(function() {
  alert('Hello!');
});

…. می‌تواند با استفاده از کامپوننت React مجددا نوشته شود.

function Button() {
  return <button id="btn">Say Hello</button>;
}

ReactDOM.render(
  <Button />,
  document.getElementById('container'),
  function() {
    $('#btn').click(function() {
      alert('Hello!');
    });
  }
);

از اینجا به بعد می‌توانید شروع به انتقال منطق بیشتر به کامپوننت و اتخاذ شیوه‌های رایج‌تر React کنید. برای مثال در کامپوننت‌ها بهتر است روی ID ها تکیه نکنید چون همان کامپوننت می‌تواند چندین مرتبه رندر شود. در مقابل، ما از سیستم رویداد React استفاده می‌کنیم و کنترل کنندۀ کلیک را مستقیما روی المان <button> ری‌اکت رجیستر می‌کنیم:

function Button(props) {
  return <button onClick={props.onClick}>Say Hello</button>;
}

function HelloButton() {
  function handleClick() {
    alert('Hello!');
  }
  return <Button onClick={handleClick} />;
}

ReactDOM.render(
  <HelloButton />,
  document.getElementById('container')
);

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

شما می‌‌توانید هر چند کامپوننت ایزوله شده‌ای که می‌خواهید داشته باشید، و از ReactDOM.render() برای رندر آنها در نگه دارنده‌های DOM متفاوت استفاده کنید. به تدریج و با تبدیل بیشتر برنامۀ خود به React، قادر خواهید بود آنها را در کامپوننت‌های بزرگتری با هم ترکیب کنید، و برخی از فراخوانی‌های ReactDOM.render() را در سلسله مراتب به بالاتر منتقل کنید.

قرار دادن ری‌اکت در یک Backbone View

به طور معمول Backbone view ها از رشته‌های HTML یا توابع الگوی تولید رشته استفاده می‌کنند تا محتوای المان DOM آنها را بسازند. این فرآیند نیز می‌تواند با رندر کردن یک کامپوننت React جایگزین شود.

در ذیل، ما یک Backbone view به نام ParagraphView خواهیم ساخت. این Backbone view تابع رندر Backbone را نادیده می‌گیرد تا یک کامپوننت <Paragraph> ری‌اکت را در ReactDOM.render() مورد استفاده، رندر کند:

function Paragraph(props) {
  return <p>{props.text}</p>;
}

const ParagraphView = Backbone.View.extend({
  render() {
    const text = this.model.get('text');
    ReactDOM.render(<Paragraph text={text} />, this.el);
    return this;
  },
  remove() {
    ReactDOM.unmountComponentAtNode(this.el);
    Backbone.View.prototype.remove.call(this);
  }
});

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

این مهم است که ما ReactDOM.unmountComponentAtNode() را نیز در متد remove فراخوانی کنیم تا React کنترل کننده‌های رویداد و منابع دیگر همراه با درخت کامپوننت را وقتی جدا شدند، unregister کند.

وقتی یک کامپوننت از درخت React حذف شد، پاکسازی به طور خودکار اجرا خواهد شد، اما از آن جایی که ما در حال حذف کل درخت به طور دستی هستیم، باید این متد را فراخوانی کنیم.

یکپارچه سازی با Model Layers

در حالی که به طور کلی توصیه می‌شود از جریان یک طرفۀ دیتا مانند حالت ری‌اکت (React state)، Flux و یا Redux استفاده کنید، کامپوننت‌های React می‌توانند از یک Model Layer از فریم‌ورک‌ها و کتابخانه‌های دیگر استفاده کنند.

استفاده از مدل‌های Backbone در کامپوننت‌های React

آسان‌ترین راه برای مصرف مدل‌ها و مجموعه‌های Backbone از یک کامپوننت React، پیروی کردن از رویدادهای مختلف تغییر و اعمال دستی یک آپدیت است.

کامپوننت‌های مسئول در مقابل رندر مدل‌ها از رویدادهای change پیروی می‌کنند در حالی که کامپوننت‌های مسئول در مقابل رندر مجموعه‌ها از رویدادهای add و remove پیروی می‌کنند. در هر دو حالت this.forceUpdate() را برای رندر مجدد کامپوننت با دیتای جدید، فراخوانی کنید.

در مثال زیر، کامپوننت List یک مجموعۀ Backbone را با استفاده از کامپوننت Item جهت رندر هر آیتم مستقل، رندر می‌کند.

class Item extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange() {
    this.forceUpdate();
  }

  componentDidMount() {
    this.props.model.on('change', this.handleChange);
  }

  componentWillUnmount() {
    this.props.model.off('change', this.handleChange);
  }

  render() {
    return <li>{this.props.model.get('text')}</li>;
  }
}

class List extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange() {
    this.forceUpdate();
  }

  componentDidMount() {
    this.props.collection.on('add', 'remove', this.handleChange);
  }

  componentWillUnmount() {
    this.props.collection.off('add', 'remove', this.handleChange);
  }

  render() {
    return (
      <ul>
        {this.props.collection.map(model => (
          <Item key={model.cid} model={model} />
        ))}
      </ul>
    );
  }
}

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

استخراج دیتا از Backbone Models

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

یک راه آن استخراج attribute های مدل به صورت دیتای ساده هنگام تغییرات، و حفظ منطق آن در یک مکان است. مثال زیر یک کامپوننت مرتبۀ بالاتر است که تمام attribute های یک مدل Backbone را داخل یک حالت استخراج کرده و دیتا را به کامپوننت بسته بندی شده منتقل می‌کند.

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

در مثال زیر، ما یک کپی از attribute های مدل می‌سازیم تا حالت داخلی را شکل دهیم. ما رویداد change را تایید می‌کنیم (و انحلال را تایید نمی‌کنیم unsubscribe on unmounting) و زمانی که این اتفاق رخ دهد، ما حالت مدل را با attribute کنونی مدل آپدیت می‌کنیم. در نهایت، مطمئن خواهیم شد که اگر prop مدل خودش تغییر کند، فراموش نخواهیم کرد که بر مدل قدیمی صحه نگذاریم (unsubscribe) و مدل جدید را تایید کنیم.

به یاد داشته باشید که این مثال، مثال کاملی در ارتباط با کار با Backbone نیست، اما این ایده را به شما می‌دهد که چگونه در یک حالت کلی با آن رو به رو شوید:

function connectToBackboneModel(WrappedComponent) {
  return class BackboneComponent extends React.Component {
    constructor(props) {
      super(props);
      this.state = Object.assign({}, props.model.attributes);
      this.handleChange = this.handleChange.bind(this);
    }

    componentDidMount() {
      this.props.model.on('change', this.handleChange);
    }

    componentWillReceiveProps(nextProps) {
      this.setState(Object.assign({}, nextProps.model.attributes));
      if (nextProps.model !== this.props.model) {
        this.props.model.off('change', this.handleChange);
        nextProps.model.on('change', this.handleChange);
      }
    }

    componentWillUnmount() {
      this.props.model.off('change', this.handleChange);
    }

    handleChange(model) {
      this.setState(model.changedAttributes());
    }

    render() {
      const propsExceptModel = Object.assign({}, this.props);
      delete propsExceptModel.model;
      return <WrappedComponent {...propsExceptModel} {...this.state} />;
    }
  }
}

برای نشان دادن نحوۀ استفاده از آن، ما یک کامپوننت NameInput را به یک مدل Backbone کانکت می‌کنیم، و firstName آن را ار مرتبه که ورودی تغییر کرد آپدیت می‌کنیم:

function NameInput(props) {
  return (
    <p>
      <input value={props.firstName} onChange={props.handleChange} />
      <br />
      My name is {props.firstName}.
    </p>
  );
}

const BackboneNameInput = connectToBackboneModel(NameInput);

function Example(props) {
  function handleChange(e) {
    props.model.set('firstName', e.target.value);
  }

  return (
    <BackboneNameInput
      model={props.model}
      handleChange={handleChange}
    />

  );
}

const model = new Backbone.Model({ firstName: 'Frodo' });
ReactDOM.render(
  <Example model={model} />,
  document.getElementById('root')
);

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

این تکنیک محدود به Backbone نمی‌شود. شما می‌توانید از React با هر مدل کتابخانه‌ای با صحه گذاشتن (subscribing) بر تغییرات آن در چرخۀ زندگی و کپی کردن دیتا در داخل حالت محلی React (اختیاری)، استفاده کنید.

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