Подтвердить что ты не робот

Могу ли я избегать forceUpdate() при использовании React with Backbone?

Реагировать на Facebook поощряет отделить изменчивое (state) и неизменяемое (props) состояние:

Постарайтесь сохранить как можно больше ваших компонентов без гражданства. Делая это, вы изолируете состояние в наиболее логичном месте и минимизируете избыточность, что упрощает рассуждение о вашем приложении.

При изменении состояния вы должны вызвать setState для запуска виртуального DOM diff, что приведет к реальному обновлению DOM только тогда, когда это необходимо.

Существует способ инициировать обновление DOM вручную, вызывая forceUpdate, но обескуражен:

Обычно следует избегать использования forceUpdate() и читать только this.props и this.state в render(). Это делает ваше приложение намного проще и эффективнее.

Однако все примеры React + Backbone, которые я видел , игнорируют этот совет и сохраняют модели и коллекции в props и вызывают forceUpdate:

В собственном примере Even React используется forceUpdate:

Есть ли лучший способ, и какие выгоды он даст?

4b9b3361

Ответ 1

Ответ Пита велик.

Магистральные модели по своей сути являются мутативными, что (хотя и не само по себе) означает, что при рендеринге вы не будете сравнивать старую версию модели. Это затрудняет интеллектуальную оптимизацию, определяя методы shouldComponentUpdate в ключевых местах ваших компонентов. (Вы также теряете способность легко хранить старые версии своей модели по другим причинам, например отменить.)

Вызов forceUpdate просто пропускает shouldComponentUpdate и заставляет компонент переименовывать. Обратите внимание, что вызов render обычно дешевый, и React по-прежнему будет касаться только DOM, если результат render изменился, поэтому проблемы с производительностью здесь не являются общими. Однако, если у вас есть выбор использовать неизменяемые данные (в том числе прохождение вокруг объектов свойств модели модели из toJSON(), как предлагает Пит), я бы очень рекомендовал его.

Ответ 2

Пока не будет лучшего ответа, позвольте мне quote Пит Хант, разработчик ядра React:

Большая победа с моделями Backbone позволила вам управлять потоком данных для вас. Когда вы позвонили set(), оно сообщит вашему приложению, что данные изменены. С помощью React вы найдете это менее необходимым, потому что все, что вам нужно сделать, это сообщить компоненту, которому принадлежит состояние через обратный вызов, и React гарантирует, что все дети обновлены. Таким образом, это часть позвоночника менее полезна ИМО (и люди, как правило, используют так называемую магистраль с React).

Вам не нужно передавать чистый JSON (хотя то, что я обычно делаю, и он хорошо работает для простых моделей данных), но вы увидите много преимуществ, если сохраните свои объекты неизменными.

Вы можете попробовать это, просто набрав toJSON() на своих базовых моделях и посмотрев, как вам это нравится, и передавайте модели вокруг.

(акцент мой)

Интересно, что Backbone.React.Component - единственный пример, который я нашел, который использует toJSON, но по какой-то причине также использует setProps вместо setState (который обескуражен).

Update

Я сделал простой микс на основе подхода Пит Хант (нет setProps, no forceUpdate):

define(function () {

  'use strict';

  var Backbone = require('backbone'),
      _ = require('underscore');

  var BackboneStateMixin = {
    getInitialState: function () {
      return this.getBackboneState(this.props);
    },

    componentDidMount: function () {
      if (!_.isFunction(this.getBackboneState)) {
        throw new Error('You must provide getBackboneState(props).');
      }

      this._bindBackboneEvents(this.props);
    },

    componentWillReceiveProps: function (newProps) {
      this._unbindBackboneEvents();
      this._bindBackboneEvents(newProps);
    },

    componentWillUnmount: function () {
      this._unbindBackboneEvents();
    },

    _updateBackboneState: function () {
      var state = this.getBackboneState(this.props);
      this.setState(state);
    },

    _bindBackboneEvents: function (props) {
      if (!_.isFunction(this.watchBackboneProps)) {
        return;
      }

      if (this._backboneListener) {
        throw new Error('Listener already exists.');
      }

      if (!props) {
        throw new Error('Passed props are empty');
      }

      var listener = _.extend({}, Backbone.Events),
          listenTo = _.partial(listener.listenTo.bind(listener), _, _, this._updateBackboneState);

      this.watchBackboneProps(props, listenTo);
      this._backboneListener = listener;
    },

    _unbindBackboneEvents: function () {
      if (!_.isFunction(this.watchBackboneProps)) {
        return;
      }

      if (!this._backboneListener) {
        throw new Error('Listener does not exist.');
      }

      this._backboneListener.stopListening();
      delete this._backboneListener;
    }
  };

  return BackboneStateMixin;

});

Неважно, какие модели или коллекции у вас есть.

Согласие на то, что модели Backbone входят в props, и их JSON автоматически помещается mixin в state. Для этого вам нужно переопределить getBackboneState(props) и, при желании, watchBackboneProps, чтобы сообщить mixin, когда вызывать setState со свежими значениями.

Пример использования:

var InfoWidget = React.createClass({
  mixins: [BackboneStateMixin, PopoverMixin],

  propTypes: {
    stampModel: React.PropTypes.instanceOf(Stamp).isRequired
  },

  // Override getBackboneState to tell the mixin
  // HOW to transform Backbone props into JSON state

  getBackboneState: function (props) {
    var stampModel = props.stampModel,
        primaryZineModel = stampModel.getPrimaryZine();

    return {
      stamp: stampModel.toJSON(),
      toggleIsLiked: stampModel.toggleIsLiked.bind(stampModel),
      primaryZine: primaryZineModel && primaryZineModel.toJSON()
    };
  },

  // Optionally override watchBackboneProps to tell the mixin
  // WHEN to transform Backbone props into JSON state

  watchBackboneProps: function (props, listenTo) {
    listenTo(props.stampModel, 'change:unauth_like_count change:is_liked');
    listenTo(props.stampModel.get('zines'), 'all');
  },

  render: function () {
    // You can use vanilla JSON values of this.state.stamp,
    // this.state.toggleIsLiked and this.state.primaryZine
    // or whatever you return from getBackboneState
    // without worrying they may point to old values
  }
}

Примечание: mixin требует Underscore 1.6.0 +.

Ответ 3

Я разработчик Backbone.React.Component. Причина, по которой мы используем setProps, состоит в том, что она предназначена только для того, чтобы ее вызывал владелец компонента (наибольший родитель). То, как я это вижу, реквизит лучше использовать для реактивных обновлений (и перейти к дочерним компонентам), чем состояние, но если вы можете указать мне некоторые причины, почему состояние лучше, я с удовольствием начну развиваться в сторону этих изменений.

Например, иногда у меня есть компоненты, которые делегируют другим, где transferPropsTo очень удобен. Использование состояния типа затрудняет достижение этого.