Skip to content

Commit

Permalink
Deprecate ReactDOM.render and ReactDOM.hydrate
Browse files Browse the repository at this point in the history
These are no longer supported in React 18. They are replaced by the
`createRoot` API.

The warning includes a link to documentation of the new API. Currently
it redirects to the corresponding working group post. Here's the PR to
set up the redirect: reactjs/react.dev#3730

Many of our tests still use ReactDOM.render. We will need to gradually
migrate them over to createRoot.

In the meantime, I added the warnings to our internal warning filter.
  • Loading branch information
acdlite committed Jun 9, 2021
1 parent c6a4957 commit 0cc374a
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 28 deletions.
12 changes: 9 additions & 3 deletions fixtures/dom/src/__tests__/wrong-act-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,15 @@ it('warns when using the wrong act version - test + dom: render', () => {
TestRenderer.act(() => {
ReactDOM.render(<App />, document.createElement('div'));
});
}).toWarnDev(["It looks like you're using the wrong act()"], {
withoutStack: true,
});
}).toWarnDev(
[
'ReactDOM.render is no longer supported in React 18.',
"It looks like you're using the wrong act()",
],
{
withoutStack: true,
}
);
});

it('warns when using the wrong act version - test + dom: updates', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,8 @@ describe('InspectedElement', () => {
});

const container = document.createElement('div');
await utils.actAsync(() =>
ReactDOM.render(<Target a={1} b="abc" />, container),
);
const root = ReactDOM.createRoot(container);
await utils.actAsync(() => root.render(<Target a={1} b="abc" />));

expect(targetRenderCount).toBe(1);
expect(console.error).toHaveBeenCalledTimes(1);
Expand Down
10 changes: 8 additions & 2 deletions packages/react-dom/src/__tests__/ReactDOMFiber-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1154,9 +1154,15 @@ describe('ReactDOMFiber', () => {
expect(ops).toEqual(['A']);

if (__DEV__) {
// TODO: this warning shouldn't be firing in the first place if user didn't call it.
const errorCalls = console.error.calls.count();
for (let i = 0; i < errorCalls; i++) {
expect(console.error.calls.argsFor(0)[0]).toMatch(
'ReactDOM.render is no longer supported in React 18',
);
expect(console.error.calls.argsFor(1)[0]).toMatch(
'ReactDOM.render is no longer supported in React 18',
);
// TODO: this warning shouldn't be firing in the first place if user didn't call it.
for (let i = 2; i < errorCalls; i++) {
expect(console.error.calls.argsFor(i)[0]).toMatch(
'unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering.',
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -787,16 +787,22 @@ describe('ReactErrorBoundaries', () => {

it('logs a single error when using error boundary', () => {
const container = document.createElement('div');
expect(() =>
ReactDOM.render(
<ErrorBoundary>
<BrokenRender />
</ErrorBoundary>,
container,
),
).toErrorDev('The above error occurred in the <BrokenRender> component:', {
logAllErrors: true,
});
spyOnDev(console, 'error');
ReactDOM.render(
<ErrorBoundary>
<BrokenRender />
</ErrorBoundary>,
container,
);
if (__DEV__) {
expect(console.error).toHaveBeenCalledTimes(2);
expect(console.error.calls.argsFor(0)[0]).toContain(
'ReactDOM.render is no longer supported',
);
expect(console.error.calls.argsFor(1)[0]).toContain(
'The above error occurred in the <BrokenRender> component:',
);
}

expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
expect(Scheduler).toHaveYielded([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ describe('ReactErrorLoggingRecovery', () => {

beforeEach(() => {
console.error = error => {
if (
typeof error === 'string' &&
error.includes('ReactDOM.render is no longer supported in React 18')
) {
// Ignore legacy root deprecation warning
return;
}
throw new Error('Buggy console.error');
};
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -668,16 +668,22 @@ describe('ReactLegacyErrorBoundaries', () => {

it('logs a single error using both error boundaries', () => {
const container = document.createElement('div');
expect(() =>
ReactDOM.render(
<BothErrorBoundaries>
<BrokenRender />
</BothErrorBoundaries>,
container,
),
).toErrorDev('The above error occurred in the <BrokenRender> component', {
logAllErrors: true,
});
spyOnDev(console, 'error');
ReactDOM.render(
<BothErrorBoundaries>
<BrokenRender />
</BothErrorBoundaries>,
container,
);
if (__DEV__) {
expect(console.error).toHaveBeenCalledTimes(2);
expect(console.error.calls.argsFor(0)[0]).toContain(
'ReactDOM.render is no longer supported',
);
expect(console.error.calls.argsFor(1)[0]).toContain(
'The above error occurred in the <BrokenRender> component:',
);
}

expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
expect(log).toEqual([
Expand Down
38 changes: 38 additions & 0 deletions packages/react-dom/src/__tests__/ReactLegacyRootWarnings-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
let ReactDOM = require('react-dom');

describe('ReactDOMRoot', () => {
let container;

beforeEach(() => {
jest.resetModules();
container = document.createElement('div');
ReactDOM = require('react-dom');
});

test('deprecation warning for ReactDOM.render', () => {
spyOnDev(console, 'error');

ReactDOM.render('Hi', container);
expect(container.textContent).toEqual('Hi');
if (__DEV__) {
expect(console.error).toHaveBeenCalledTimes(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'ReactDOM.render is no longer supported',
);
}
});

test('deprecation warning for ReactDOM.hydrate', () => {
spyOnDev(console, 'error');

container.innerHTML = 'Hi';
ReactDOM.hydrate('Hi', container);
expect(container.textContent).toEqual('Hi');
if (__DEV__) {
expect(console.error).toHaveBeenCalledTimes(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'ReactDOM.hydrate is no longer supported',
);
}
});
});
18 changes: 18 additions & 0 deletions packages/react-dom/src/client/ReactDOMLegacy.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,15 @@ export function hydrate(
container: Container,
callback: ?Function,
) {
if (__DEV__) {
console.error(
'ReactDOM.hydrate is no longer supported in React 18. Use createRoot ' +
'instead. Until you switch to the new API, your app will behave as ' +
"if it's running React 17. Learn " +
'more: https://reactjs.org/link/switch-to-createroot',
);
}

invariant(
isValidContainer(container),
'Target container is not a DOM element.',
Expand Down Expand Up @@ -250,6 +259,15 @@ export function render(
container: Container,
callback: ?Function,
) {
if (__DEV__) {
console.error(
'ReactDOM.render is no longer supported in React 18. Use createRoot ' +
'instead. Until you switch to the new API, your app will behave as ' +
"if it's running React 17. Learn " +
'more: https://reactjs.org/link/switch-to-createroot',
);
}

invariant(
isValidContainer(container),
'Target container is not a DOM element.',
Expand Down
10 changes: 10 additions & 0 deletions scripts/jest/shouldIgnoreConsoleError.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ module.exports = function shouldIgnoreConsoleError(format, args) {
// Ignore it too.
return true;
}
if (
format.indexOf('ReactDOM.render is no longer supported in React 18') !==
-1 ||
format.indexOf(
'ReactDOM.hydrate is no longer supported in React 18'
) !== -1
) {
// We haven't finished migrating our tests to use createRoot.
return true;
}
}
} else {
if (
Expand Down

0 comments on commit 0cc374a

Please sign in to comment.