diff --git a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js index 51b901f1dd58c..2aa3ef8db0715 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js @@ -16,6 +16,11 @@ let ReactDOM; const AsyncMode = React.unstable_AsyncMode; +const setUntrackedInputValue = Object.getOwnPropertyDescriptor( + HTMLInputElement.prototype, + 'value', +).set; + describe('ReactDOMFiberAsync', () => { let container; @@ -47,6 +52,12 @@ describe('ReactDOMFiberAsync', () => { jest.resetModules(); container = document.createElement('div'); ReactDOM = require('react-dom'); + + document.body.appendChild(container); + }); + + afterEach(() => { + document.body.removeChild(container); }); it('renders synchronously by default', () => { @@ -60,6 +71,49 @@ describe('ReactDOMFiberAsync', () => { expect(ops).toEqual(['Hi', 'Bye']); }); + it('does not perform deferred updates synchronously', () => { + let inputRef = React.createRef(); + let contentRef = React.createRef(); + + class Counter extends React.Component { + state = {asyncValue: ''}; + + handleChange = e => { + const nextValue = e.target.value; + ReactDOM.unstable_deferredUpdates(() => { + this.setState({ + asyncValue: nextValue, + }); + }); + }; + + render() { + return ( +
+ +

{this.state.asyncValue}

+
+ ); + } + } + ReactDOM.render(, container); + expect(contentRef.current.textContent).toBe(''); + + setUntrackedInputValue.call(inputRef.current, 'hello'); + inputRef.current.dispatchEvent(new MouseEvent('input', {bubbles: true})); + + // Should not flush yet because it's a deferred update. + expect(contentRef.current.textContent).toBe(''); + + // Should flush now. + jest.runAllTimers(); + expect(contentRef.current.textContent).toBe('hello'); + }); + describe('with feature flag disabled', () => { beforeEach(() => { jest.resetModules();