Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate ReactDOM.render and ReactDOM.hydrate #21652

Merged
merged 2 commits into from Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 9 additions & 3 deletions fixtures/dom/src/__tests__/wrong-act-test.js
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
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
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
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
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
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
@@ -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',
);
}
});
});
Expand Up @@ -10,6 +10,7 @@
'use strict';

const stream = require('stream');
const shouldIgnoreConsoleError = require('../../../../../scripts/jest/shouldIgnoreConsoleError');
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gross but oh well


module.exports = function(initModules) {
let ReactDOM;
Expand Down Expand Up @@ -74,23 +75,29 @@ module.exports = function(initModules) {
}

const result = await fn();
if (
console.error.calls &&
console.error.calls.count() !== count &&
console.error.calls.count() !== 0
) {
console.log(
`We expected ${count} warning(s), but saw ${console.error.calls.count()} warning(s).`,
);
if (console.error.calls.count() > 0) {
console.log(`We saw these warnings:`);
for (let i = 0; i < console.error.calls.count(); i++) {
console.log(...console.error.calls.argsFor(i));
if (console.error.calls && console.error.calls.count() !== 0) {
const filteredWarnings = [];
for (let i = 0; i < console.error.calls.count(); i++) {
const args = console.error.calls.argsFor(i);
const [format, ...rest] = args;
if (!shouldIgnoreConsoleError(format, rest)) {
filteredWarnings.push(args);
}
}
if (filteredWarnings.length !== count) {
console.log(
`We expected ${count} warning(s), but saw ${filteredWarnings.length} warning(s).`,
);
if (filteredWarnings.count > 0) {
console.log(`We saw these warnings:`);
for (let i = 0; i < filteredWarnings.length; i++) {
console.log(...filteredWarnings[i]);
}
}
if (__DEV__) {
expect(console.error).toHaveBeenCalledTimes(count);
}
}
}
if (__DEV__) {
expect(console.error).toHaveBeenCalledTimes(count);
}
return result;
}
Expand Down
18 changes: 18 additions & 0 deletions packages/react-dom/src/client/ReactDOMLegacy.js
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 ' +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it weird that we say "no longer supported in React 18" as if it was ever supported in React 18?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReactDOM.hydrate is deprecated in React 18.

'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 ' +
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use "ReactDOM.render is deprecated in React 18" instead for consistent messaging? "not supported" sounds like it's no longer working. Unless the other deprecation warnings also use "not supported" instead of "deprecated".

'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
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