Use createRoot in ReactDOMComponentTree-test (#28112)
Jack Pope committed
Jan 26, 2024 at 17:18 UTC
4c41c09ccc28e5cf85cbb706908b740c95ff5a97
2 files changed
+145
-98
packages/react-dom/src/__tests__/ReactDOMComponentTree-test.js
+87
-98
@@ -11,12 +11,15 @@
11
12
describe('ReactDOMComponentTree', () => {
13
let React;
14
- let ReactDOM;
14
+ let ReactDOMClient;
15
+ let act;
16
let container;
17
18
beforeEach(() => {
19
React = require('react');
19
- ReactDOM = require('react-dom');
20
+ ReactDOMClient = require('react-dom/client');
21
+ act = require('internal-test-utils').act;
22
+
23
container = document.createElement('div');
24
document.body.appendChild(container);
25
});
@@ -26,7 +29,7 @@ describe('ReactDOMComponentTree', () => {
29
container = null;
30
});
31
29
- it('finds nodes for instances on events', () => {
32
+ it('finds nodes for instances on events', async () => {
33
const mouseOverID = 'mouseOverID';
34
const clickID = 'clickID';
35
let currentTargetID = null;
@@ -34,17 +37,16 @@ describe('ReactDOMComponentTree', () => {
37
// when an event is dispatched so we can test behavior by invoking
38
// events on elements in the tree and confirming the expected node is
39
// set as the current target
37
- class Component extends React.Component {
38
- handler = e => {
40
+ function Component() {
41
+ const handler = e => {
42
currentTargetID = e.currentTarget.id;
43
};
41
- render() {
42
- return (
43
- <div id={mouseOverID} onMouseOver={this.handler}>
44
- <div id={clickID} onClick={this.handler} />
45
- </div>
46
- );
47
- }
44
+
45
+ return (
46
+ <div id={mouseOverID} onMouseOver={handler}>
47
+ <div id={clickID} onClick={handler} />
48
+ </div>
49
+ );
50
}
51
52
function simulateMouseEvent(elem, type) {
@@ -54,8 +56,10 @@ describe('ReactDOMComponentTree', () => {
56
elem.dispatchEvent(event);
57
}
58
57
- const component = <Component />;
58
- ReactDOM.render(component, container);
59
+ const root = ReactDOMClient.createRoot(container);
60
+ await act(() => {
61
+ root.render(<Component />);
62
+ });
63
expect(currentTargetID).toBe(null);
64
simulateMouseEvent(document.getElementById(mouseOverID), 'mouseover');
65
expect(currentTargetID).toBe(mouseOverID);
@@ -63,25 +67,24 @@ describe('ReactDOMComponentTree', () => {
67
expect(currentTargetID).toBe(clickID);
68
});
69
66
- it('finds closest instance for node when an event happens', () => {
70
+ it('finds closest instance for node when an event happens', async () => {
71
const nonReactElemID = 'aID';
72
const innerHTML = {__html: `<div id="${nonReactElemID}"></div>`};
73
const closestInstanceID = 'closestInstance';
74
let currentTargetID = null;
75
72
- class ClosestInstance extends React.Component {
73
- _onClick = e => {
76
+ function ClosestInstance() {
77
+ const onClick = e => {
78
currentTargetID = e.currentTarget.id;
79
};
76
- render() {
77
- return (
78
- <div
79
- id={closestInstanceID}
80
- onClick={this._onClick}
81
- dangerouslySetInnerHTML={innerHTML}
82
- />
83
- );
84
- }
80
+
81
+ return (
82
+ <div
83
+ id={closestInstanceID}
84
+ onClick={onClick}
85
+ dangerouslySetInnerHTML={innerHTML}
86
+ />
87
+ );
88
}
89
90
function simulateClick(elem) {
@@ -91,16 +94,22 @@ describe('ReactDOMComponentTree', () => {
94
elem.dispatchEvent(event);
95
}
96
94
- const component = <ClosestInstance />;
95
- ReactDOM.render(<section>{component}</section>, container);
97
+ const root = ReactDOMClient.createRoot(container);
98
+ await act(() => {
99
+ root.render(
100
+ <section>
101
+ <ClosestInstance />
102
+ </section>,
103
+ );
104
+ });
105
expect(currentTargetID).toBe(null);
106
simulateClick(document.getElementById(nonReactElemID));
107
expect(currentTargetID).toBe(closestInstanceID);
108
});
109
101
- it('updates event handlers from fiber props', () => {
110
+ it('updates event handlers from fiber props', async () => {
111
let action = '';
103
- let instance;
112
+ let flip;
113
const handlerA = () => (action = 'A');
114
const handlerB = () => (action = 'B');
115
@@ -111,55 +120,56 @@ describe('ReactDOMComponentTree', () => {
120
target.dispatchEvent(event);
121
}
122
114
- class HandlerFlipper extends React.Component {
115
- state = {flip: false};
116
- flip() {
117
- this.setState({flip: true});
118
- }
119
- render() {
120
- return (
121
- <div
122
- id="update"
123
- onMouseOver={this.state.flip ? handlerB : handlerA}
124
- />
125
- );
126
- }
123
+ function HandlerFlipper() {
124
+ const [flipVal, setFlipVal] = React.useState(false);
125
+ flip = () => setFlipVal(true);
126
+
127
+ return <div id="update" onMouseOver={flipVal ? handlerB : handlerA} />;
128
}
129
129
- ReactDOM.render(
130
- <HandlerFlipper key="1" ref={n => (instance = n)} />,
131
- container,
132
- );
130
+ const root = ReactDOMClient.createRoot(container);
131
+ await act(() => {
132
+ root.render(<HandlerFlipper key="1" />);
133
+ });
134
const node = container.firstChild;
134
- simulateMouseOver(node);
135
+
136
+ await act(() => {
137
+ simulateMouseOver(node);
138
+ });
139
expect(action).toEqual('A');
140
action = '';
141
+
142
// Render with the other event handler.
138
- instance.flip();
139
- simulateMouseOver(node);
143
+ await act(() => {
144
+ flip();
145
+ });
146
+ await act(() => {
147
+ simulateMouseOver(node);
148
+ });
149
expect(action).toEqual('B');
150
});
151
143
- it('finds a controlled instance from node and gets its current fiber props', () => {
152
+ it('finds a controlled instance from node and gets its current fiber props', async () => {
153
+ let inputRef;
154
const inputID = 'inputID';
155
const startValue = undefined;
156
const finishValue = 'finish';
157
148
- class Controlled extends React.Component {
149
- state = {value: startValue};
150
- a = null;
151
- _onChange = e => this.setState({value: e.currentTarget.value});
152
- render() {
153
- return (
154
- <input
155
- id={inputID}
156
- type="text"
157
- ref={n => (this.a = n)}
158
- value={this.state.value}
159
- onChange={this._onChange}
160
- />
161
- );
162
- }
158
+ function Controlled() {
159
+ const [state, setState] = React.useState(startValue);
160
+ const ref = React.useRef();
161
+ inputRef = ref;
162
+ const onChange = e => setState(e.currentTarget.value);
163
+
164
+ return (
165
+ <input
166
+ id={inputID}
167
+ type="text"
168
+ ref={ref}
169
+ value={state}
170
+ onChange={onChange}
171
+ />
172
+ );
173
}
174
175
const setUntrackedInputValue = Object.getOwnPropertyDescriptor(
@@ -175,9 +185,17 @@ describe('ReactDOMComponentTree', () => {
185
elem.dispatchEvent(inputEvent);
186
}
187
178
- const component = <Controlled />;
179
- const instance = ReactDOM.render(component, container);
180
- expect(() => simulateInput(instance.a, finishValue)).toErrorDev(
188
+ const root = ReactDOMClient.createRoot(container);
189
+ await act(() => {
190
+ root.render(<Controlled />);
191
+ });
192
+
193
+ await expect(
194
+ async () =>
195
+ await act(() => {
196
+ simulateInput(inputRef.current, finishValue);
197
+ }),
198
+ ).toErrorDev(
199
'Warning: A component is changing an uncontrolled input to be controlled. ' +
200
'This is likely caused by the value changing from undefined to ' +
201
'a defined value, which should not happen. ' +
@@ -186,33 +204,4 @@ describe('ReactDOMComponentTree', () => {
204
'https://reactjs.org/link/controlled-components',
205
);
206
});
189
-
190
- it('finds instance of node that is attempted to be unmounted', () => {
191
- const component = <div />;
192
- const node = ReactDOM.render(<div>{component}</div>, container);
193
- expect(() => ReactDOM.unmountComponentAtNode(node)).toErrorDev(
194
- "unmountComponentAtNode(): The node you're attempting to unmount " +
195
- 'was rendered by React and is not a top-level container. You may ' +
196
- 'have accidentally passed in a React root node instead of its ' +
197
- 'container.',
198
- {withoutStack: true},
199
- );
200
- });
201
-
202
- it('finds instance from node to stop rendering over other react rendered components', () => {
203
- const component = (
204
- <div>
205
- <span>Hello</span>
206
- </div>
207
- );
208
- const anotherComponent = <div />;
209
- const instance = ReactDOM.render(component, container);
210
- expect(() => ReactDOM.render(anotherComponent, instance)).toErrorDev(
211
- 'render(...): Replacing React-rendered children with a new root ' +
212
- 'component. If you intended to update the children of this node, ' +
213
- 'you should instead have the existing children update their state ' +
214
- 'and render the new components instead of calling ReactDOM.render.',
215
- {withoutStack: true},
216
- );
217
- });
207
});
packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js
new
+58
@@ -0,0 +1,58 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @emails react-core
8
+ */
9
+
10
+'use strict';
11
+
12
+describe('ReactDOMComponentTree', () => {
13
+ let React;
14
+ let ReactDOM;
15
+ let container;
16
+
17
+ beforeEach(() => {
18
+ React = require('react');
19
+ ReactDOM = require('react-dom');
20
+
21
+ container = document.createElement('div');
22
+ document.body.appendChild(container);
23
+ });
24
+
25
+ afterEach(() => {
26
+ document.body.removeChild(container);
27
+ container = null;
28
+ });
29
+
30
+ it('finds instance of node that is attempted to be unmounted', () => {
31
+ const component = <div />;
32
+ const node = ReactDOM.render(<div>{component}</div>, container);
33
+ expect(() => ReactDOM.unmountComponentAtNode(node)).toErrorDev(
34
+ "unmountComponentAtNode(): The node you're attempting to unmount " +
35
+ 'was rendered by React and is not a top-level container. You may ' +
36
+ 'have accidentally passed in a React root node instead of its ' +
37
+ 'container.',
38
+ {withoutStack: true},
39
+ );
40
+ });
41
+
42
+ it('finds instance from node to stop rendering over other react rendered components', () => {
43
+ const component = (
44
+ <div>
45
+ <span>Hello</span>
46
+ </div>
47
+ );
48
+ const anotherComponent = <div />;
49
+ const instance = ReactDOM.render(component, container);
50
+ expect(() => ReactDOM.render(anotherComponent, instance)).toErrorDev(
51
+ 'render(...): Replacing React-rendered children with a new root ' +
52
+ 'component. If you intended to update the children of this node, ' +
53
+ 'you should instead have the existing children update their state ' +
54
+ 'and render the new components instead of calling ReactDOM.render.',
55
+ {withoutStack: true},
56
+ );
57
+ });
58
+});