@@ -175,27 +175,6 @@ describe('ReactDOMRoot', () => {
175
175
) ;
176
176
} ) ;
177
177
178
- it ( 'clears existing children with legacy API' , async ( ) => {
179
- container . innerHTML = '<div>a</div><div>b</div>' ;
180
- ReactDOM . render (
181
- < div >
182
- < span > c</ span >
183
- < span > d</ span >
184
- </ div > ,
185
- container ,
186
- ) ;
187
- expect ( container . textContent ) . toEqual ( 'cd' ) ;
188
- ReactDOM . render (
189
- < div >
190
- < span > d</ span >
191
- < span > c</ span >
192
- </ div > ,
193
- container ,
194
- ) ;
195
- await waitForAll ( [ ] ) ;
196
- expect ( container . textContent ) . toEqual ( 'dc' ) ;
197
- } ) ;
198
-
199
178
it ( 'clears existing children' , async ( ) => {
200
179
container . innerHTML = '<div>a</div><div>b</div>' ;
201
180
const root = ReactDOMClient . createRoot ( container ) ;
@@ -223,122 +202,6 @@ describe('ReactDOMRoot', () => {
223
202
} ) . toThrow ( 'createRoot(...): Target container is not a DOM element.' ) ;
224
203
} ) ;
225
204
226
- it ( 'warns when rendering with legacy API into createRoot() container' , async ( ) => {
227
- const root = ReactDOMClient . createRoot ( container ) ;
228
- root . render ( < div > Hi</ div > ) ;
229
- await waitForAll ( [ ] ) ;
230
- expect ( container . textContent ) . toEqual ( 'Hi' ) ;
231
- expect ( ( ) => {
232
- ReactDOM . render ( < div > Bye</ div > , container ) ;
233
- } ) . toErrorDev (
234
- [
235
- // We care about this warning:
236
- 'You are calling ReactDOM.render() on a container that was previously ' +
237
- 'passed to ReactDOMClient.createRoot(). This is not supported. ' +
238
- 'Did you mean to call root.render(element)?' ,
239
- // This is more of a symptom but restructuring the code to avoid it isn't worth it:
240
- 'Replacing React-rendered children with a new root component.' ,
241
- ] ,
242
- { withoutStack : true } ,
243
- ) ;
244
- await waitForAll ( [ ] ) ;
245
- // This works now but we could disallow it:
246
- expect ( container . textContent ) . toEqual ( 'Bye' ) ;
247
- } ) ;
248
-
249
- it ( 'warns when hydrating with legacy API into createRoot() container' , async ( ) => {
250
- const root = ReactDOMClient . createRoot ( container ) ;
251
- root . render ( < div > Hi</ div > ) ;
252
- await waitForAll ( [ ] ) ;
253
- expect ( container . textContent ) . toEqual ( 'Hi' ) ;
254
- expect ( ( ) => {
255
- ReactDOM . hydrate ( < div > Hi</ div > , container ) ;
256
- } ) . toErrorDev (
257
- [
258
- // We care about this warning:
259
- 'You are calling ReactDOM.hydrate() on a container that was previously ' +
260
- 'passed to ReactDOMClient.createRoot(). This is not supported. ' +
261
- 'Did you mean to call hydrateRoot(container, element)?' ,
262
- // This is more of a symptom but restructuring the code to avoid it isn't worth it:
263
- 'Replacing React-rendered children with a new root component.' ,
264
- ] ,
265
- { withoutStack : true } ,
266
- ) ;
267
- } ) ;
268
-
269
- it ( 'callback passed to legacy hydrate() API' , ( ) => {
270
- container . innerHTML = '<div>Hi</div>' ;
271
- ReactDOM . hydrate ( < div > Hi</ div > , container , ( ) => {
272
- Scheduler . log ( 'callback' ) ;
273
- } ) ;
274
- expect ( container . textContent ) . toEqual ( 'Hi' ) ;
275
- assertLog ( [ 'callback' ] ) ;
276
- } ) ;
277
-
278
- it ( 'warns when unmounting with legacy API (no previous content)' , async ( ) => {
279
- const root = ReactDOMClient . createRoot ( container ) ;
280
- root . render ( < div > Hi</ div > ) ;
281
- await waitForAll ( [ ] ) ;
282
- expect ( container . textContent ) . toEqual ( 'Hi' ) ;
283
- let unmounted = false ;
284
- expect ( ( ) => {
285
- unmounted = ReactDOM . unmountComponentAtNode ( container ) ;
286
- } ) . toErrorDev (
287
- [
288
- // We care about this warning:
289
- 'You are calling ReactDOM.unmountComponentAtNode() on a container that was previously ' +
290
- 'passed to ReactDOMClient.createRoot(). This is not supported. Did you mean to call root.unmount()?' ,
291
- // This is more of a symptom but restructuring the code to avoid it isn't worth it:
292
- "The node you're attempting to unmount was rendered by React and is not a top-level container." ,
293
- ] ,
294
- { withoutStack : true } ,
295
- ) ;
296
- expect ( unmounted ) . toBe ( false ) ;
297
- await waitForAll ( [ ] ) ;
298
- expect ( container . textContent ) . toEqual ( 'Hi' ) ;
299
- root . unmount ( ) ;
300
- await waitForAll ( [ ] ) ;
301
- expect ( container . textContent ) . toEqual ( '' ) ;
302
- } ) ;
303
-
304
- it ( 'warns when unmounting with legacy API (has previous content)' , async ( ) => {
305
- // Currently createRoot().render() doesn't clear this.
306
- container . appendChild ( document . createElement ( 'div' ) ) ;
307
- // The rest is the same as test above.
308
- const root = ReactDOMClient . createRoot ( container ) ;
309
- root . render ( < div > Hi</ div > ) ;
310
- await waitForAll ( [ ] ) ;
311
- expect ( container . textContent ) . toEqual ( 'Hi' ) ;
312
- let unmounted = false ;
313
- expect ( ( ) => {
314
- unmounted = ReactDOM . unmountComponentAtNode ( container ) ;
315
- } ) . toErrorDev (
316
- [
317
- 'Did you mean to call root.unmount()?' ,
318
- // This is more of a symptom but restructuring the code to avoid it isn't worth it:
319
- "The node you're attempting to unmount was rendered by React and is not a top-level container." ,
320
- ] ,
321
- { withoutStack : true } ,
322
- ) ;
323
- expect ( unmounted ) . toBe ( false ) ;
324
- await waitForAll ( [ ] ) ;
325
- expect ( container . textContent ) . toEqual ( 'Hi' ) ;
326
- root . unmount ( ) ;
327
- await waitForAll ( [ ] ) ;
328
- expect ( container . textContent ) . toEqual ( '' ) ;
329
- } ) ;
330
-
331
- it ( 'warns when passing legacy container to createRoot()' , ( ) => {
332
- ReactDOM . render ( < div > Hi</ div > , container ) ;
333
- expect ( ( ) => {
334
- ReactDOMClient . createRoot ( container ) ;
335
- } ) . toErrorDev (
336
- 'You are calling ReactDOMClient.createRoot() on a container that was previously ' +
337
- 'passed to ReactDOM.render(). This is not supported.' ,
338
- { withoutStack : true } ,
339
- ) ;
340
- } ) ;
341
-
342
205
it ( 'warns when creating two roots managing the same container' , ( ) => {
343
206
ReactDOMClient . createRoot ( container ) ;
344
207
expect ( ( ) => {
@@ -399,6 +262,80 @@ describe('ReactDOMRoot', () => {
399
262
}
400
263
} ) ;
401
264
265
+ it ( 'should render different components in same root' , async ( ) => {
266
+ document . body . appendChild ( container ) ;
267
+ const root = ReactDOMClient . createRoot ( container ) ;
268
+
269
+ await act ( ( ) => {
270
+ root . render ( < div /> ) ;
271
+ } ) ;
272
+ expect ( container . firstChild . nodeName ) . toBe ( 'DIV' ) ;
273
+
274
+ await act ( ( ) => {
275
+ root . render ( < span /> ) ;
276
+ } ) ;
277
+ expect ( container . firstChild . nodeName ) . toBe ( 'SPAN' ) ;
278
+ } ) ;
279
+
280
+ it ( 'should not warn if mounting into non-empty node' , async ( ) => {
281
+ container . innerHTML = '<div></div>' ;
282
+ const root = ReactDOMClient . createRoot ( container ) ;
283
+ await act ( ( ) => {
284
+ root . render ( < div /> ) ;
285
+ } ) ;
286
+
287
+ expect ( true ) . toBe ( true ) ;
288
+ } ) ;
289
+
290
+ it ( 'should reuse markup if rendering to the same target twice' , async ( ) => {
291
+ const root = ReactDOMClient . createRoot ( container ) ;
292
+ await act ( ( ) => {
293
+ root . render ( < div /> ) ;
294
+ } ) ;
295
+ const firstElm = container . firstChild ;
296
+ await act ( ( ) => {
297
+ root . render ( < div /> ) ;
298
+ } ) ;
299
+
300
+ expect ( firstElm ) . toBe ( container . firstChild ) ;
301
+ } ) ;
302
+
303
+ it ( 'should unmount and remount if the key changes' , async ( ) => {
304
+ function Component ( { text} ) {
305
+ useEffect ( ( ) => {
306
+ Scheduler . log ( 'Mount' ) ;
307
+
308
+ return ( ) => {
309
+ Scheduler . log ( 'Unmount' ) ;
310
+ } ;
311
+ } , [ ] ) ;
312
+
313
+ return < span > { text } </ span > ;
314
+ }
315
+
316
+ const root = ReactDOMClient . createRoot ( container ) ;
317
+
318
+ await act ( ( ) => {
319
+ root . render ( < Component text = "orange" key = "A" /> ) ;
320
+ } ) ;
321
+ expect ( container . firstChild . innerHTML ) . toBe ( 'orange' ) ;
322
+ assertLog ( [ 'Mount' ] ) ;
323
+
324
+ // If we change the key, the component is unmounted and remounted
325
+ await act ( ( ) => {
326
+ root . render ( < Component text = "green" key = "B" /> ) ;
327
+ } ) ;
328
+ expect ( container . firstChild . innerHTML ) . toBe ( 'green' ) ;
329
+ assertLog ( [ 'Unmount' , 'Mount' ] ) ;
330
+
331
+ // But if we don't change the key, the component instance is reused
332
+ await act ( ( ) => {
333
+ root . render ( < Component text = "blue" key = "B" /> ) ;
334
+ } ) ;
335
+ expect ( container . firstChild . innerHTML ) . toBe ( 'blue' ) ;
336
+ assertLog ( [ ] ) ;
337
+ } ) ;
338
+
402
339
it ( 'throws if unmounting a root that has had its contents removed' , async ( ) => {
403
340
const root = ReactDOMClient . createRoot ( container ) ;
404
341
await act ( ( ) => {
@@ -514,9 +451,6 @@ describe('ReactDOMRoot', () => {
514
451
expect ( ( ) => ReactDOMClient . hydrateRoot ( commentNode ) ) . toThrow (
515
452
'hydrateRoot(...): Target container is not a DOM element.' ,
516
453
) ;
517
-
518
- // Still works in the legacy API
519
- ReactDOM . render ( < div /> , commentNode ) ;
520
454
} ) ;
521
455
522
456
it ( 'warn if no children passed to hydrateRoot' , async ( ) => {
@@ -539,4 +473,23 @@ describe('ReactDOMRoot', () => {
539
473
} ,
540
474
) ;
541
475
} ) ;
476
+
477
+ it ( 'warns when given a function' , ( ) => {
478
+ function Component ( ) {
479
+ return < div /> ;
480
+ }
481
+
482
+ const root = ReactDOMClient . createRoot ( document . createElement ( 'div' ) ) ;
483
+
484
+ expect ( ( ) => {
485
+ ReactDOM . flushSync ( ( ) => {
486
+ root . render ( Component ) ;
487
+ } ) ;
488
+ } ) . toErrorDev (
489
+ 'Functions are not valid as a React child. ' +
490
+ 'This may happen if you return a Component instead of <Component /> from render. ' +
491
+ 'Or maybe you meant to call this function rather than return it.' ,
492
+ { withoutStack : true } ,
493
+ ) ;
494
+ } ) ;
542
495
} ) ;
0 commit comments