clayallsopp / react.backbone

Plugin for React to make Backbone migration easier
MIT License
839 stars 60 forks source link

Support for dynamically subscribing to a Backbone Model or Collection? #29

Open iamdanfox opened 10 years ago

iamdanfox commented 10 years ago

Components often need to react to Backbone models that are introduced during the component's lifecycle. (e.g. a model returned by a function that is stored in this.state).

At present, I just inline the crucial bits from your subscribe and unsubscribe functions. It would be really cool if there was a way to dynamically add another React mixin or somehow 'subscribe' to these models without needing to rewrite it each time

markijbema commented 10 years ago

Could you show a piece of code where you do this? I think it would be good to bind to the result of a function, instead of to a string referring to a property. Problem is, if it just occurs sometimes in a function, it is hard to guess the lifecycle beforehand; should it unbind on props updating? On state changing? etc.

iamdanfox commented 10 years ago

I can't paste the code here, but I was writing a FilteredSearchComponent that received a search string and displayed a filtered subset of results. Each result was represented as a backbone model. They were loaded from the server and would magically update with push updates.

In my scenario, I needed the FilteredSearchComponent to re-render if any of the result models changed (because that might change whether they passed the filter).

I did the unbinding at the componentWillUnmount stage since a few unnecessary render calls work fine with React :)

markijbema commented 10 years ago

Was there a specific reason you were enable to bind on 'change' of the collection?

iamdanfox commented 10 years ago

That would work perfectly if the result models were returned in a collection (and some logic was moved out of the component). Unfortunately, that isn't always possible, because those resultModels might then end up appearing in multiple collections, which doesn't have great support as far as I can tell...

In all honesty, I think this is quite a small issue, but might be an interesting consideration for future development / refactoring.

I paraphrased the component code below (compiled js also available)...

FilteredSearchComponent = React.createClass
  propTypes:
    filterString: React.PropTypes.string.isRequired

  getInitialState: ->
    loading: true
    results: []

  componentWillMount: ->
    listOfModels = App.dataLayer.checkOutAllPushModels()
    for model in listOfModels # would like to get rid of this... listen to all and autounbind
      model.on 'change', (=>@forceUpdate()), @
    @setState
      loading: false
      results: listOfModels

  componentWillUnMount: ->
    for model in @state.results
      App.dataLayer.returnPushModel model
      model.off 'all', null, @   # would like to get rid of this too 

  render: ->
    matchesFilterString = (model) ->
      return model.matchesFilterString(@props.filterString)

    <div>
      { if @state.loading
          <Spinner />
        else
          @state.resultModels.filter(matchesFilterString).map (model) -> 
            <ResultRow model={model} /> }
    </div>