Backbone: Collection does not purge internal `_byId` reference to a removed model

Created on 22 Sep 2017  ·  3Comments  ·  Source: jashkenas/backbone

Please find below about the tersest reproduction I could fathom of a sequence of steps in a Backbone application that leads to a Collection getting into a state of internal confusion.

A working copy can be found in this JSFiddle .

What it demonstrates is a collection keeping an internal (_byId) reference to a remove()-ed Model. That Model is no longer part of collection.models, however, it can still be retrieved through its (former) id.

var
    MyView = Backbone.View.extend( {

        initialize: function () {

            this.model = new Backbone.Model( {

                id:  'foo'
            ,   foo: 'bar'
            } );

            this.collection = new Backbone.Collection( this.model );

            this.listenTo( this.model, 'change:id', function ( model, id ) {

                if ( id == null ) {
                    this.collection.remove( model );
                }

            } );

            this.model.unset( 'id' );

            $('#models' ).val( JSON.stringify( this.collection.models      ));
            $('#_byId'  ).val( JSON.stringify( this.collection._byId       ));
            $('#get-foo').val( JSON.stringify( this.collection.get( 'foo' )));
        }

    })

, foo = new MyView()
;
bug

Most helpful comment

Yes — you should never do this, but it's still a bug.

We should fix this by making sure that if an id is changing from a value to null, we delete this._byId[id] first.

All 3 comments

Hmm, interesting. When we add model to collection, __byId_ add two properties: model.id and model.cid, their reference to model. Issue on 1134 line, id === undefined, but __byId_ contains model.id - _'foo'_.

Collection don't has some special logic when change model.id to value == null, check line 1183.

Also you can change unset to set _null_ or _undefined_. this.model.set('id', null);

P.S.: I think you never need set your model id to null or undefined, it is anti pattern.

backbone_remove

backbone_model_event

Yes — you should never do this, but it's still a bug.

We should fix this by making sure that if an id is changing from a value to null, we delete this._byId[id] first.

If you remove a model from a collection without the use of an event, it works as expected. The problem is in _onModelEvent which checks if the name of the event is change, when the actual event name being sent in this case is change:id.

Was this page helpful?
0 / 5 - 0 ratings