/*** Copyright (c) Meta Platforms, Inc. and affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.** @emails react-core*/'use strict';
let React;
let ReactDOM;
let ReactDOMClient;
let act;
let waitForAll;
describe('ReactDOMHooks', () => {
let container;
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
act = require('internal-test-utils').act;
waitForAll = require('internal-test-utils').waitForAll;
container = document.createElement('div');
document.body.appendChild(container);
});afterEach(() => {
document.body.removeChild(container);
});it('can ReactDOM.render() from useEffect', async () => {
const container2 = document.createElement('div');
const container3 = document.createElement('div');
function Example1({n}) {
React.useEffect(() => {
ReactDOM.render(<Example2 n={n} />, container2);
});return 1 * n;
}function Example2({n}) {
React.useEffect(() => {
ReactDOM.render(<Example3 n={n} />, container3);
});return 2 * n;
}function Example3({n}) {
return 3 * n;
}ReactDOM.render(<Example1 n={1} />, container);
expect(container.textContent).toBe('1');
expect(container2.textContent).toBe('');
expect(container3.textContent).toBe('');
await waitForAll([]);
expect(container.textContent).toBe('1');
expect(container2.textContent).toBe('2');
expect(container3.textContent).toBe('3');
ReactDOM.render(<Example1 n={2} />, container);
expect(container.textContent).toBe('2');
expect(container2.textContent).toBe('2'); // Not flushed yet
expect(container3.textContent).toBe('3'); // Not flushed yet
await waitForAll([]);
expect(container.textContent).toBe('2');
expect(container2.textContent).toBe('4');
expect(container3.textContent).toBe('6');
});it('should not bail out when an update is scheduled from within an event handler', () => {
const {createRef, useCallback, useState} = React;
const Example = ({inputRef, labelRef}) => {
const [text, setText] = useState('');
const handleInput = useCallback(event => {
setText(event.target.value);
});return (
<><input ref={inputRef} onInput={handleInput} />
<label ref={labelRef}>{text}</label>
</>
);
};const inputRef = createRef();
const labelRef = createRef();
ReactDOM.render(
<Example inputRef={inputRef} labelRef={labelRef} />,
container,
);inputRef.current.value = 'abc';
inputRef.current.dispatchEvent(
new Event('input', {bubbles: true, cancelable: true}),
);expect(labelRef.current.innerHTML).toBe('abc');
});it('should not bail out when an update is scheduled from within an event handler in Concurrent Mode', async () => {
const {createRef, useCallback, useState} = React;
const Example = ({inputRef, labelRef}) => {
const [text, setText] = useState('');
const handleInput = useCallback(event => {
setText(event.target.value);
});return (
<><input ref={inputRef} onInput={handleInput} />
<label ref={labelRef}>{text}</label>
</>
);
};const inputRef = createRef();
const labelRef = createRef();
const root = ReactDOMClient.createRoot(container);
root.render(<Example inputRef={inputRef} labelRef={labelRef} />);
await waitForAll([]);
inputRef.current.value = 'abc';
await act(() => {
inputRef.current.dispatchEvent(
new Event('input', {
bubbles: true,
cancelable: true,
}),);});expect(labelRef.current.innerHTML).toBe('abc');
});});