Skip to content

Commit ff35461

Browse files
bvaughnrhagigi
authored andcommitted
Fixed descrepancy between host and class component refs (facebook#12178)
When a ref is removed from a class component, React now calls the previous ref-setter (if there was one) with null. Previously this was the case only for host component refs. A new test has been added.
1 parent 4a20ff2 commit ff35461

File tree

2 files changed

+46
-10
lines changed

2 files changed

+46
-10
lines changed

packages/react-dom/src/__tests__/refs-destruction-test.js

+42-9
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,31 @@ describe('refs-destruction', () => {
2323
ReactDOM = require('react-dom');
2424
ReactTestUtils = require('react-dom/test-utils');
2525

26+
class ClassComponent extends React.Component {
27+
render() {
28+
return null;
29+
}
30+
}
31+
2632
TestComponent = class extends React.Component {
2733
render() {
28-
return (
29-
<div>
30-
{this.props.destroy ? null : (
31-
<div ref="theInnerDiv">Lets try to destroy this.</div>
32-
)}
33-
</div>
34-
);
34+
if (this.props.destroy) {
35+
return <div />;
36+
} else if (this.props.removeRef) {
37+
return (
38+
<div>
39+
<div />
40+
<ClassComponent />
41+
</div>
42+
);
43+
} else {
44+
return (
45+
<div>
46+
<div ref="theInnerDiv" />
47+
<ClassComponent ref="theInnerClassComponent" />
48+
</div>
49+
);
50+
}
3551
}
3652
};
3753
});
@@ -45,7 +61,7 @@ describe('refs-destruction', () => {
4561
expect(
4662
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
4763
.length,
48-
).toEqual(1);
64+
).toEqual(2);
4965
ReactDOM.unmountComponentAtNode(container);
5066
expect(
5167
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
@@ -62,14 +78,31 @@ describe('refs-destruction', () => {
6278
expect(
6379
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
6480
.length,
65-
).toEqual(1);
81+
).toEqual(2);
6682
ReactDOM.render(<TestComponent destroy={true} />, container);
6783
expect(
6884
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
6985
.length,
7086
).toEqual(0);
7187
});
7288

89+
it('should remove refs when removing the child ref attribute', () => {
90+
const container = document.createElement('div');
91+
const testInstance = ReactDOM.render(<TestComponent />, container);
92+
expect(ReactTestUtils.isDOMComponent(testInstance.refs.theInnerDiv)).toBe(
93+
true,
94+
);
95+
expect(
96+
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
97+
.length,
98+
).toEqual(2);
99+
ReactDOM.render(<TestComponent removeRef={true} />, container);
100+
expect(
101+
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
102+
.length,
103+
).toEqual(0);
104+
});
105+
73106
it('should not error when destroying child with ref asynchronously', () => {
74107
class Modal extends React.Component {
75108
componentDidMount() {

packages/react-reconciler/src/ReactFiberBeginWork.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,10 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
184184

185185
function markRef(current: Fiber | null, workInProgress: Fiber) {
186186
const ref = workInProgress.ref;
187-
if (ref !== null && (!current || current.ref !== ref)) {
187+
if (
188+
(current === null && ref !== null) ||
189+
(current !== null && current.ref !== ref)
190+
) {
188191
// Schedule a Ref effect
189192
workInProgress.effectTag |= Ref;
190193
}

0 commit comments

Comments
 (0)