Skip to content

Commit

Permalink
Merge pull request #5237 from spicyj/gh-5125
Browse files Browse the repository at this point in the history
Make sure top-level callback has correct context
(cherry picked from commit b0a7a00)
  • Loading branch information
sophiebits authored and zpao committed Oct 28, 2015
1 parent 12cafa7 commit eadbf33
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 3 deletions.
11 changes: 8 additions & 3 deletions src/renderers/dom/client/ReactMount.js
Original file line number Diff line number Diff line change
Expand Up @@ -635,12 +635,17 @@ var ReactMount = {
var prevWrappedElement = prevComponent._currentElement;
var prevElement = prevWrappedElement.props;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
return ReactMount._updateRootComponent(
var publicInst = prevComponent._renderedComponent.getPublicInstance();
var updatedCallback = callback && function() {
callback.call(publicInst);
};
ReactMount._updateRootComponent(
prevComponent,
nextWrappedElement,
container,
callback
)._renderedComponent.getPublicInstance();
updatedCallback
);
return publicInst;
} else {
ReactMount.unmountComponentAtNode(container);
}
Expand Down
95 changes: 95 additions & 0 deletions src/renderers/dom/client/__tests__/ReactMount-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,99 @@ describe('ReactMount', function() {
'render the new components instead of calling ReactDOM.render.'
);
});

it('should not crash in node cache when unmounting', function() {
var Component = React.createClass({
render: function() {
// Add refs to some nodes so that they get traversed and cached
return (
<div ref="a">
<div ref="b">b</div>
{this.props.showC && <div>c</div>}
</div>
);
},
});

var container = document.createElement('container');

ReactDOM.render(<div><Component showC={false} /></div>, container);

// Right now, A and B are in the cache. When we add C, it won't get added to
// the cache (assuming markup-string mode).
ReactDOM.render(<div><Component showC={true} /></div>, container);

// Remove A, B, and C. Unmounting C shouldn't cause B to get recached.
ReactDOM.render(<div></div>, container);

// Add them back -- this shouldn't cause a cached node collision.
ReactDOM.render(<div><Component showC={true} /></div>, container);

ReactDOM.unmountComponentAtNode(container);
});

it('should not crash in node cache when unmounting, case 2', function() {
var A = React.createClass({
render: function() {
return <a key={this.props.innerKey}>{this.props.innerKey}</a>;
},
});
var Component = React.createClass({
render: function() {
return (
<b>
<i>{this.props.step === 1 && <q />}</i>
{this.props.step === 1 && <A innerKey={this.props.step} />}
</b>
);
},
});

var container = document.createElement('container');

ReactDOM.render(<Component step={1} />, container);
ReactDOM.render(<Component step={2} />, container);
ReactDOM.render(<Component step={1} />, container);
ReactMount.getID(container.querySelector('a'));
});

it('passes the correct callback context', function() {
var container = document.createElement('div');
var calls = 0;

ReactDOM.render(<div />, container, function() {
expect(this.nodeName).toBe('DIV');
calls++;
});

// Update, no type change
ReactDOM.render(<div />, container, function() {
expect(this.nodeName).toBe('DIV');
calls++;
});

// Update, type change
ReactDOM.render(<span />, container, function() {
expect(this.nodeName).toBe('SPAN');
calls++;
});

// Batched update, no type change
ReactDOM.unstable_batchedUpdates(function() {
ReactDOM.render(<span />, container, function() {
expect(this.nodeName).toBe('SPAN');
calls++;
});
});

// Batched update, type change
ReactDOM.unstable_batchedUpdates(function() {
ReactDOM.render(<article />, container, function() {
expect(this.nodeName).toBe('ARTICLE');
calls++;
});
});

expect(calls).toBe(5);
});
});

0 comments on commit eadbf33

Please sign in to comment.