.',
+ ],
+ {withoutStack: 1},
+ );
+ });
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
-
,
+ // @gate experimental
+ it('useOpaqueIdentifier warns if you try to use the result as a string in a child component wrapped in a Suspense', async () => {
+ function Child({appId}) {
+ return
;
+ }
+ function App() {
+ const id = useOpaqueIdentifier();
+ return (
+
+
+
);
+ }
- if (gate(flags => flags.deferRenderPhaseUpdateToNextBatch)) {
- expect(() => Scheduler.unstable_flushAll()).toErrorDev([
- 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
- 'Do not read the value directly.',
- ]);
- } else {
- // This error isn't surfaced to the user; only the warning is.
- // The error is just the mechanism that restarts the render.
- expect(() =>
- expect(() => Scheduler.unstable_flushAll()).toThrow(
- 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
- 'Do not read the value directly.',
- ),
- ).toErrorDev([
+ const container = document.createElement('div');
+ document.body.appendChild(container);
+
+ container.innerHTML = ReactDOMServer.renderToString(
);
+
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
);
+
+ if (gate(flags => flags.deferRenderPhaseUpdateToNextBatch)) {
+ expect(() => Scheduler.unstable_flushAll()).toErrorDev([
+ 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
+ 'Do not read the value directly.',
+ ]);
+ } else {
+ // This error isn't surfaced to the user; only the warning is.
+ // The error is just the mechanism that restarts the render.
+ expect(() =>
+ expect(() => Scheduler.unstable_flushAll()).toThrow(
'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
'Do not read the value directly.',
- ]);
- }
- });
+ ),
+ ).toErrorDev([
+ 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
+ 'Do not read the value directly.',
+ ]);
+ }
+ });
- it('useOpaqueIdentifier warns if you try to add the result as a number in a child component wrapped in a Suspense', async () => {
- function Child({appId}) {
- return
;
- }
- function App() {
- const [show] = useState(false);
- const id = useOpaqueIdentifier();
- return (
-
- {show && }
-
-
- );
- }
+ // @gate experimental
+ it('useOpaqueIdentifier warns if you try to add the result as a number in a child component wrapped in a Suspense', async () => {
+ function Child({appId}) {
+ return
;
+ }
+ function App() {
+ const [show] = useState(false);
+ const id = useOpaqueIdentifier();
+ return (
+
+ {show && }
+
+
+ );
+ }
- const container = document.createElement('div');
- document.body.appendChild(container);
+ const container = document.createElement('div');
+ document.body.appendChild(container);
- container.innerHTML = ReactDOMServer.renderToString(
);
+ container.innerHTML = ReactDOMServer.renderToString(
);
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
-
,
- );
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
);
- if (gate(flags => flags.deferRenderPhaseUpdateToNextBatch)) {
- expect(() => Scheduler.unstable_flushAll()).toErrorDev([
- 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
- 'Do not read the value directly.',
- ]);
- } else {
- // This error isn't surfaced to the user; only the warning is.
- // The error is just the mechanism that restarts the render.
- expect(() =>
- expect(() => Scheduler.unstable_flushAll()).toThrow(
- 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
- 'Do not read the value directly.',
- ),
- ).toErrorDev([
+ if (gate(flags => flags.deferRenderPhaseUpdateToNextBatch)) {
+ expect(() => Scheduler.unstable_flushAll()).toErrorDev([
+ 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
+ 'Do not read the value directly.',
+ ]);
+ } else {
+ // This error isn't surfaced to the user; only the warning is.
+ // The error is just the mechanism that restarts the render.
+ expect(() =>
+ expect(() => Scheduler.unstable_flushAll()).toThrow(
'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
'Do not read the value directly.',
- ]);
- }
- });
-
- it('useOpaqueIdentifier with two opaque identifiers on the same page', () => {
- let _setShow;
+ ),
+ ).toErrorDev([
+ 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
+ 'Do not read the value directly.',
+ ]);
+ }
+ });
- function App() {
- const id1 = useOpaqueIdentifier();
- const id2 = useOpaqueIdentifier();
- const [show, setShow] = useState(true);
- _setShow = setShow;
+ // @gate experimental
+ it('useOpaqueIdentifier with two opaque identifiers on the same page', () => {
+ let _setShow;
- return (
-
-
- {show ? (
- {'Child'}
- ) : (
- {'Child'}
- )}
-
- {'test'}
-
- );
- }
+ function App() {
+ const id1 = useOpaqueIdentifier();
+ const id2 = useOpaqueIdentifier();
+ const [show, setShow] = useState(true);
+ _setShow = setShow;
- const container = document.createElement('div');
- document.body.appendChild(container);
+ return (
+
+
+ {show ? (
+ {'Child'}
+ ) : (
+ {'Child'}
+ )}
+
+ {'test'}
+
+ );
+ }
- container.innerHTML = ReactDOMServer.renderToString(
);
+ const container = document.createElement('div');
+ document.body.appendChild(container);
- const serverID = container
- .getElementsByTagName('span')[0]
- .getAttribute('id');
- expect(serverID).not.toBeNull();
- expect(
- container
- .getElementsByTagName('span')[1]
- .getAttribute('aria-labelledby'),
- ).toEqual(serverID);
+ container.innerHTML = ReactDOMServer.renderToString(
);
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
-
,
- );
- jest.runAllTimers();
- expect(Scheduler).toHaveYielded([]);
- expect(Scheduler).toFlushAndYield([]);
+ const serverID = container
+ .getElementsByTagName('span')[0]
+ .getAttribute('id');
+ expect(serverID).not.toBeNull();
+ expect(
+ container
+ .getElementsByTagName('span')[1]
+ .getAttribute('aria-labelledby'),
+ ).toEqual(serverID);
- ReactTestUtils.act(() => {
- _setShow(false);
- });
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
);
+ jest.runAllTimers();
+ expect(Scheduler).toHaveYielded([]);
+ expect(Scheduler).toFlushAndYield([]);
- expect(
- container
- .getElementsByTagName('span')[1]
- .getAttribute('aria-labelledby'),
- ).toEqual(serverID);
- expect(
- container.getElementsByTagName('span')[0].getAttribute('id'),
- ).not.toEqual(serverID);
- expect(
- container.getElementsByTagName('span')[0].getAttribute('id'),
- ).not.toBeNull();
+ ReactTestUtils.act(() => {
+ _setShow(false);
});
+
+ expect(
+ container
+ .getElementsByTagName('span')[1]
+ .getAttribute('aria-labelledby'),
+ ).toEqual(serverID);
+ expect(
+ container.getElementsByTagName('span')[0].getAttribute('id'),
+ ).not.toEqual(serverID);
+ expect(
+ container.getElementsByTagName('span')[0].getAttribute('id'),
+ ).not.toBeNull();
});
- }
+ });
});
diff --git a/packages/react-dom/src/__tests__/utils/ReactDOMServerIntegrationTestUtils.js b/packages/react-dom/src/__tests__/utils/ReactDOMServerIntegrationTestUtils.js
index 30fddb8f94934..33fd350cb6e6f 100644
--- a/packages/react-dom/src/__tests__/utils/ReactDOMServerIntegrationTestUtils.js
+++ b/packages/react-dom/src/__tests__/utils/ReactDOMServerIntegrationTestUtils.js
@@ -144,9 +144,11 @@ module.exports = function(initModules) {
async function renderIntoStream(reactElement, errorCount = 0) {
return await expectErrors(
() =>
- new Promise(resolve => {
+ new Promise((resolve, reject) => {
const writable = new DrainWritable();
- ReactDOMServer.renderToNodeStream(reactElement).pipe(writable);
+ const s = ReactDOMServer.renderToNodeStream(reactElement);
+ s.on('error', e => reject(e));
+ s.pipe(writable);
writable.on('finish', () => resolve(writable.buffer));
}),
errorCount,