mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
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.
This commit is contained in:
@@ -420,4 +420,27 @@ describe('ReactDOMRoot', () => {
|
||||
// Still works in the legacy API
|
||||
ReactDOM.render(<div />, commentNode);
|
||||
});
|
||||
|
||||
it('warn if no children passed to hydrateRoot', async () => {
|
||||
expect(() =>
|
||||
ReactDOM.hydrateRoot(container),
|
||||
).toErrorDev(
|
||||
'Must provide initial children as second argument to hydrateRoot.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
|
||||
it('warn if JSX passed to createRoot', async () => {
|
||||
function App() {
|
||||
return 'Child';
|
||||
}
|
||||
|
||||
expect(() => ReactDOM.createRoot(container, <App />)).toErrorDev(
|
||||
'You passed a JSX element to createRoot. You probably meant to call ' +
|
||||
'root.render instead',
|
||||
{
|
||||
withoutStack: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+24
@@ -15,6 +15,7 @@ import type {
|
||||
} from 'react-reconciler/src/ReactInternalTypes';
|
||||
|
||||
import {queueExplicitHydrationTarget} from '../events/ReactDOMEventReplaying';
|
||||
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
|
||||
|
||||
export type RootType = {
|
||||
render(children: ReactNodeList): void,
|
||||
@@ -174,6 +175,20 @@ export function createRoot(
|
||||
console.warn(
|
||||
'hydrate through createRoot is deprecated. Use ReactDOM.hydrateRoot(container, <App />) instead.',
|
||||
);
|
||||
} else {
|
||||
if (
|
||||
typeof options === 'object' &&
|
||||
options !== null &&
|
||||
(options: any).$$typeof === REACT_ELEMENT_TYPE
|
||||
) {
|
||||
console.error(
|
||||
'You passed a JSX element to createRoot. You probably meant to ' +
|
||||
'call root.render instead. ' +
|
||||
'Example usage:\n\n' +
|
||||
' let root = createRoot(domContainer);\n' +
|
||||
' root.render(<App />);',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.unstable_strictMode === true) {
|
||||
@@ -237,6 +252,15 @@ export function hydrateRoot(
|
||||
|
||||
warnIfReactDOMContainerInDEV(container);
|
||||
|
||||
if (__DEV__) {
|
||||
if (initialChildren === undefined) {
|
||||
console.error(
|
||||
'Must provide initial children as second argument to hydrateRoot. ' +
|
||||
'Example usage: hydrateRoot(domContainer, <App />)',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// For now we reuse the whole bag of options since they contain
|
||||
// the hydration callbacks.
|
||||
const hydrationCallbacks = options != null ? options : null;
|
||||
|
||||
Reference in New Issue
Block a user