Khan / react-components

khan.github.io/react-components/
MIT License
1.01k stars 99 forks source link

backbone-mixin #19

Open lynnaloo opened 10 years ago

lynnaloo commented 10 years ago

If the backbone-mixin is deprecated, then what are you using in it's place? I'm not sure if this is part of what was abandoned, but I'm wondering in the example why the Backbone model is a prop instead of in the state? Doesn't that force you to have to setProps on the parent and forceUpdate on the component?

joelburget commented 10 years ago

The backbone mixin is deprecated for essentially the reason you mention - any change on the model causes a forceUpdate on the component.

Internally we do have a version that uses state. It requires a bit more configuration because you have to tell it how to update state when the model changes, but I think that tradeoff is well worth it. I don't think it's gotten much use since we're gradually moving away from backbone backing our views to simple objects / flux.

I'll leave this open until I add a note about this.

gaearon commented 10 years ago

This is the mixin we used (until we transitioned to Flux as well):

'use strict';

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

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

  componentDidMount: function () {
    this._handleBackboneChange = _.throttle(this._handleBackboneChange, 16, {
      leading: true,
      trailing: true
    });

    if (!_.isFunction(this.watchBackboneProps)) {
      throw new Error('You must provide watchBackboneProps(props, listenTo).');
    }

    this._bindBackboneEvents(this.props);
  },

  componentWillReceiveProps: function (nextProps) {
    var haveBackbonePropsChanged = true;

    if (_.isFunction(this.haveBackbonePropsChanged)) {
      haveBackbonePropsChanged = this.haveBackbonePropsChanged(nextProps);
    }

    if (haveBackbonePropsChanged) {
      this._bindBackboneEvents(nextProps);
    }
  },

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

  _bindBackboneEvents: function (props) {
    this._unbindBackboneEvents();

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

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

  _unbindBackboneEvents: function () {
    if (this._backboneListener) {
      this._backboneListener.stopListening();
      delete this._backboneListener;
    }
  },

  _queueHandleBackboneChange: function () {
    _.defer(this._handleBackboneChange);
  },

  _handleBackboneChange: function () {
    if (!this.isMounted()) {
      return;
    }

    if (_.isFunction(this.getBackboneState)) {
      this.setState(this.getBackboneState(this.props));
    } else {
      this.forceUpdate();
    }
  }
};

module.exports = BackboneStateMixin;

Components define watchBackboneProps(props, listenTo), getBackboneState(props), optionally haveBackbonePropsChanged(nextProps).