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

Add ReactDOMClient to ServerIntegrationElements #28134

Merged
merged 4 commits into from
Feb 1, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegratio
const {disableInputAttributeSyncing} = require('shared/ReactFeatureFlags');

let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let ReactTestUtils;

function initModules() {
// Reset warning cache.
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMClient,
ReactDOMServer,
ReactTestUtils,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@
const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');

let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let ReactTestUtils;

function initModules() {
// Reset warning cache.
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMClient,
ReactDOMServer,
ReactTestUtils,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,23 @@ const TEXT_NODE_TYPE = 3;

let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let ReactFeatureFlags;
let ReactTestUtils;

function initModules() {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactTestUtils = require('react-dom/test-utils');

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMClient,
ReactDOMServer,
ReactTestUtils,
};
Expand Down Expand Up @@ -136,7 +140,13 @@ describe('ReactDOMServerIntegration', () => {
// DOM nodes on the client side. We force it to fire early
// so that it gets deduplicated later, and doesn't fail the test.
expect(() => {
ReactDOM.render(<nonstandard />, document.createElement('div'));
ReactDOM.flushSync(() => {
const root = ReactDOMClient.createRoot(
document.createElement('div'),
);

root.render(<nonstandard />);
});
}).toErrorDev('The tag <nonstandard> is unrecognized in this browser.');

const e = await render(<nonstandard>Text</nonstandard>);
Expand Down Expand Up @@ -833,15 +843,21 @@ describe('ReactDOMServerIntegration', () => {
'an element with one text child with special characters',
async render => {
const e = await render(<div>{'foo\rbar\r\nbaz\nqux\u0000'}</div>);
if (render === serverRender || render === streamRender) {
if (
render === serverRender ||
render === streamRender ||
(render === clientRenderOnServerString &&
ReactFeatureFlags.enableClientRenderFallbackOnTextMismatch)
) {
expect(e.childNodes.length).toBe(1);
// Everything becomes LF when parsed from server HTML.
// Everything becomes LF when parsed from server HTML or hydrated if enableClientRenderFallbackOnTextMismatch is on.
// Null character is ignored.
expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\nbar\nbaz\nqux');
} else {
expect(e.childNodes.length).toBe(1);
// Client rendering (or hydration) uses JS value with CR.
// Client rendering (or hydration without enableClientRenderFallbackOnTextMismatch) uses JS value with CR.
// Null character stays.

expectNode(
e.childNodes[0],
TEXT_NODE_TYPE,
Expand All @@ -860,17 +876,23 @@ describe('ReactDOMServerIntegration', () => {
{'\r\nbaz\nqux\u0000'}
</div>,
);
if (render === serverRender || render === streamRender) {
if (
render === serverRender ||
render === streamRender ||
(render === clientRenderOnServerString &&
ReactFeatureFlags.enableClientRenderFallbackOnTextMismatch)
) {
// We have three nodes because there is a comment between them.
expect(e.childNodes.length).toBe(3);
// Everything becomes LF when parsed from server HTML.
// Everything becomes LF when parsed from server HTML or hydrated if enableClientRenderFallbackOnTextMismatch is on.
// Null character is ignored.
expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\nbar');
expectNode(e.childNodes[2], TEXT_NODE_TYPE, '\nbaz\nqux');
} else if (render === clientRenderOnServerString) {
// We have three nodes because there is a comment between them.
expect(e.childNodes.length).toBe(3);
// Hydration uses JS value with CR and null character.
// Hydration without enableClientRenderFallbackOnTextMismatch uses JS value with CR and null character.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hydrating with the new root without enableClientRenderFallbackOnTextMismatch (www) is like old root i.e. replaces server HTML with client side rendered CR.
Hydrating with the new root with enableClientRenderFallbackOnTextMismatch is keeping the server HTML.

Looks like not patching up the server HTML here was intentionally added in #26391

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice explanation, thanks for the fix!


expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\rbar');
expectNode(e.childNodes[2], TEXT_NODE_TYPE, '\r\nbaz\nqux\u0000');
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@
const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');

let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let ReactTestUtils;

function initModules() {
// Reset warning cache.
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMClient,
ReactDOMServer,
ReactTestUtils,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegratio
const {disableInputAttributeSyncing} = require('shared/ReactFeatureFlags');

let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let ReactTestUtils;

function initModules() {
// Reset warning cache.
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMClient,
ReactDOMServer,
ReactTestUtils,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@
const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');

let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let ReactTestUtils;

function initModules() {
// Reset warning cache.
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMClient,
ReactDOMServer,
ReactTestUtils,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@
const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');

let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let ReactTestUtils;

function initModules() {
// Reset warning cache.
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMClient,
ReactDOMServer,
ReactTestUtils,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');

let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let ReactTestUtils;
let forwardRef;
Expand All @@ -26,7 +26,7 @@ function initModules() {
// Reset warning cache.
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');
forwardRef = React.forwardRef;
Expand All @@ -44,7 +44,7 @@ function initModules() {

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMClient,
ReactDOMServer,
ReactTestUtils,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@
const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');

let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let ReactTestUtils;

function initModules() {
// Reset warning cache.
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMClient,
ReactDOMServer,
ReactTestUtils,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@
const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');

let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let ReactTestUtils;

function initModules() {
// Reset warning cache.
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMClient,
ReactDOMServer,
ReactTestUtils,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@
const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');

let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let ReactTestUtils;

function initModules() {
// Reset warning cache.
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMClient,
ReactDOMServer,
ReactTestUtils,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ const shouldIgnoreConsoleError = require('../../../../../scripts/jest/shouldIgno

module.exports = function (initModules) {
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let act;

function resetModules() {
({ReactDOM, ReactDOMServer} = initModules());
({ReactDOM, ReactDOMClient, ReactDOMServer} = initModules());
act = require('internal-test-utils').act;
}

Expand Down Expand Up @@ -51,11 +52,24 @@ module.exports = function (initModules) {
async function asyncReactDOMRender(reactElement, domElement, forceHydrate) {
if (forceHydrate) {
await act(() => {
ReactDOM.hydrate(reactElement, domElement);
if (ReactDOMClient) {
ReactDOMClient.hydrateRoot(domElement, reactElement, {
onRecoverableError: () => {
// TODO: assert on recoverable error count.
},
});
} else {
ReactDOM.hydrate(reactElement, domElement);
}
});
} else {
await act(() => {
ReactDOM.render(reactElement, domElement);
if (ReactDOMClient) {
const root = ReactDOMClient.createRoot(domElement);
root.render(reactElement);
} else {
ReactDOM.render(reactElement, domElement);
}
});
}
}
Expand All @@ -80,7 +94,11 @@ module.exports = function (initModules) {
for (let i = 0; i < console.error.mock.calls.length; i++) {
const args = console.error.mock.calls[i];
const [format, ...rest] = args;
if (!shouldIgnoreConsoleError(format, rest)) {
if (
!shouldIgnoreConsoleError(format, rest, {
TODO_ignoreHydrationErrors: true,
})
) {
filteredWarnings.push(args);
}
}
Expand Down