@samitouri / QOS-React-2 / commits / 8c4cd65cfa

Add warnings for common root API mistakes (#23356)

For createRoot, a common mistake is to pass JSX as the second argument, instead of calling root.render. For hydrateRoot, a common mistake is to forget to pass children as the second argument. The type system will enforce correct usage, but since not everyone uses types we'll log a helpful warning, too.

Andrew Clark committed Feb 24, 2022 at 10:57 UTC 8c4cd65cfaa4614bac7fd7783b4ec502a337eba3
2 files changed +47
packages/react-dom/src/__tests__/ReactDOMRoot-test.js
+23
@@ -420,4 +420,27 @@ describe('ReactDOMRoot', () => {
420 // Still works in the legacy API
421 ReactDOM.render(<div />, commentNode);
422 });
423 +
424 + it('warn if no children passed to hydrateRoot', async () => {
425 + expect(() =>
426 + ReactDOM.hydrateRoot(container),
427 + ).toErrorDev(
428 + 'Must provide initial children as second argument to hydrateRoot.',
429 + {withoutStack: true},
430 + );
431 + });
432 +
433 + it('warn if JSX passed to createRoot', async () => {
434 + function App() {
435 + return 'Child';
436 + }
437 +
438 + expect(() => ReactDOM.createRoot(container, <App />)).toErrorDev(
439 + 'You passed a JSX element to createRoot. You probably meant to call ' +
440 + 'root.render instead',
441 + {
442 + withoutStack: true,
443 + },
444 + );
445 + });
446 });
packages/react-dom/src/client/ReactDOMRoot.js
+24
@@ -15,6 +15,7 @@ import type {
15 } from 'react-reconciler/src/ReactInternalTypes';
16
17 import {queueExplicitHydrationTarget} from '../events/ReactDOMEventReplaying';
18 +import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
19
20 export type RootType = {
21 render(children: ReactNodeList): void,
@@ -174,6 +175,20 @@ export function createRoot(
175 console.warn(
176 'hydrate through createRoot is deprecated. Use ReactDOM.hydrateRoot(container, <App />) instead.',
177 );
178 + } else {
179 + if (
180 + typeof options === 'object' &&
181 + options !== null &&
182 + (options: any).$$typeof === REACT_ELEMENT_TYPE
183 + ) {
184 + console.error(
185 + 'You passed a JSX element to createRoot. You probably meant to ' +
186 + 'call root.render instead. ' +
187 + 'Example usage:\n\n' +
188 + ' let root = createRoot(domContainer);\n' +
189 + ' root.render(<App />);',
190 + );
191 + }
192 }
193 }
194 if (options.unstable_strictMode === true) {
@@ -237,6 +252,15 @@ export function hydrateRoot(
252
253 warnIfReactDOMContainerInDEV(container);
254
255 + if (__DEV__) {
256 + if (initialChildren === undefined) {
257 + console.error(
258 + 'Must provide initial children as second argument to hydrateRoot. ' +
259 + 'Example usage: hydrateRoot(domContainer, <App />)',
260 + );
261 + }
262 + }
263 +
264 // For now we reuse the whole bag of options since they contain
265 // the hydration callbacks.
266 const hydrationCallbacks = options != null ? options : null;