Skip to content

Commit 81da909

Browse files
gaearonjetoneza
authored andcommitted
Remove buggy unstable_deferredUpdates() (facebook#13488)
* Add a failing test for deferredUpdates not being respected * Don't consider deferred updates interactive * Remove now-unnecessary setTimeout hack in the fixture * Remove unstable_deferredUpdates
1 parent e370ff1 commit 81da909

5 files changed

Lines changed: 90 additions & 34 deletions

File tree

fixtures/unstable-async/suspense/src/components/App.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React, {Placeholder, PureComponent} from 'react';
2-
import {unstable_deferredUpdates} from 'react-dom';
32
import {createResource} from 'simple-cache-provider';
43
import {cache} from '../cache';
54
import Spinner from './Spinner';
@@ -31,7 +30,7 @@ export default class App extends PureComponent {
3130
this.setState({
3231
currentId: id,
3332
});
34-
unstable_deferredUpdates(() => {
33+
requestIdleCallback(() => {
3534
this.setState({
3635
showDetail: true,
3736
});

fixtures/unstable-async/time-slicing/src/index.js

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import React, {PureComponent, unstable_AsyncMode} from 'react';
2-
import {flushSync, render, unstable_deferredUpdates} from 'react-dom';
1+
import React, {PureComponent} from 'react';
2+
import {flushSync, render} from 'react-dom';
33
import _ from 'lodash';
44
import Charts from './Charts';
55
import Clock from './Clock';
@@ -54,22 +54,21 @@ class App extends PureComponent {
5454
return;
5555
}
5656
if (this.state.strategy !== 'async') {
57-
this.setState(state => ({
58-
showDemo: !state.showDemo,
59-
}));
57+
flushSync(() => {
58+
this.setState(state => ({
59+
showDemo: !state.showDemo,
60+
}));
61+
});
6062
return;
6163
}
6264
if (this._ignoreClick) {
6365
return;
6466
}
6567
this._ignoreClick = true;
6668

67-
// TODO: needing setTimeout here seems like a React bug.
68-
setTimeout(() => {
69-
unstable_deferredUpdates(() => {
70-
this.setState({showDemo: true}, () => {
71-
this._ignoreClick = false;
72-
});
69+
requestIdleCallback(() => {
70+
this.setState({showDemo: true}, () => {
71+
this._ignoreClick = false;
7372
});
7473
});
7574
};
@@ -107,11 +106,8 @@ class App extends PureComponent {
107106
this.debouncedHandleChange(value);
108107
break;
109108
case 'async':
110-
// TODO: needing setTimeout here seems like a React bug.
111-
setTimeout(() => {
112-
unstable_deferredUpdates(() => {
113-
this.setState({value});
114-
});
109+
requestIdleCallback(() => {
110+
this.setState({value});
115111
});
116112
break;
117113
default:
@@ -120,8 +116,6 @@ class App extends PureComponent {
120116
};
121117

122118
render() {
123-
const Wrapper =
124-
this.state.strategy === 'async' ? unstable_AsyncMode : 'div';
125119
const {showClock} = this.state;
126120
const data = this.getStreamData(this.state.value);
127121
return (
@@ -137,20 +131,23 @@ class App extends PureComponent {
137131
defaultValue={this.state.input}
138132
onChange={this.handleChange}
139133
/>
140-
<Wrapper>
141-
<div className="demo" onClick={this.handleChartClick}>
142-
{this.state.showDemo && (
143-
<Charts data={data} onClick={this.handleChartClick} />
144-
)}
145-
<div style={{display: showClock ? 'block' : 'none'}}>
146-
<Clock />
147-
</div>
134+
<div className="demo" onClick={this.handleChartClick}>
135+
{this.state.showDemo && (
136+
<Charts data={data} onClick={this.handleChartClick} />
137+
)}
138+
<div style={{display: showClock ? 'block' : 'none'}}>
139+
<Clock />
148140
</div>
149-
</Wrapper>
141+
</div>
150142
</div>
151143
);
152144
}
153145
}
154146

155147
const container = document.getElementById('root');
156-
render(<App />, container);
148+
render(
149+
<React.unstable_AsyncMode>
150+
<App />
151+
</React.unstable_AsyncMode>,
152+
container
153+
);

packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ let ReactDOM;
1616

1717
const AsyncMode = React.unstable_AsyncMode;
1818

19+
const setUntrackedInputValue = Object.getOwnPropertyDescriptor(
20+
HTMLInputElement.prototype,
21+
'value',
22+
).set;
23+
1924
describe('ReactDOMFiberAsync', () => {
2025
let container;
2126

@@ -47,6 +52,12 @@ describe('ReactDOMFiberAsync', () => {
4752
jest.resetModules();
4853
container = document.createElement('div');
4954
ReactDOM = require('react-dom');
55+
56+
document.body.appendChild(container);
57+
});
58+
59+
afterEach(() => {
60+
document.body.removeChild(container);
5061
});
5162

5263
it('renders synchronously by default', () => {
@@ -60,11 +71,60 @@ describe('ReactDOMFiberAsync', () => {
6071
expect(ops).toEqual(['Hi', 'Bye']);
6172
});
6273

74+
it('does not perform deferred updates synchronously', () => {
75+
let inputRef = React.createRef();
76+
let asyncValueRef = React.createRef();
77+
let syncValueRef = React.createRef();
78+
79+
class Counter extends React.Component {
80+
state = {asyncValue: '', syncValue: ''};
81+
82+
handleChange = e => {
83+
const nextValue = e.target.value;
84+
requestIdleCallback(() => {
85+
this.setState({
86+
asyncValue: nextValue,
87+
});
88+
});
89+
this.setState({
90+
syncValue: nextValue,
91+
});
92+
};
93+
94+
render() {
95+
return (
96+
<div>
97+
<input
98+
ref={inputRef}
99+
onChange={this.handleChange}
100+
defaultValue=""
101+
/>
102+
<p ref={asyncValueRef}>{this.state.asyncValue}</p>
103+
<p ref={syncValueRef}>{this.state.syncValue}</p>
104+
</div>
105+
);
106+
}
107+
}
108+
ReactDOM.render(<Counter />, container);
109+
expect(asyncValueRef.current.textContent).toBe('');
110+
expect(syncValueRef.current.textContent).toBe('');
111+
112+
setUntrackedInputValue.call(inputRef.current, 'hello');
113+
inputRef.current.dispatchEvent(new MouseEvent('input', {bubbles: true}));
114+
// Should only flush non-deferred update.
115+
expect(asyncValueRef.current.textContent).toBe('');
116+
expect(syncValueRef.current.textContent).toBe('hello');
117+
118+
// Should flush both updates now.
119+
jest.runAllTimers();
120+
expect(asyncValueRef.current.textContent).toBe('hello');
121+
expect(syncValueRef.current.textContent).toBe('hello');
122+
});
123+
63124
describe('with feature flag disabled', () => {
64125
beforeEach(() => {
65126
jest.resetModules();
66127
ReactFeatureFlags = require('shared/ReactFeatureFlags');
67-
container = document.createElement('div');
68128
ReactDOM = require('react-dom');
69129
});
70130

@@ -91,7 +151,6 @@ describe('ReactDOMFiberAsync', () => {
91151
beforeEach(() => {
92152
jest.resetModules();
93153
ReactFeatureFlags = require('shared/ReactFeatureFlags');
94-
container = document.createElement('div');
95154
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
96155
ReactDOM = require('react-dom');
97156
});

packages/react-dom/src/client/ReactDOM.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -732,8 +732,6 @@ const ReactDOM: Object = {
732732

733733
unstable_batchedUpdates: DOMRenderer.batchedUpdates,
734734

735-
unstable_deferredUpdates: DOMRenderer.deferredUpdates,
736-
737735
unstable_interactiveUpdates: DOMRenderer.interactiveUpdates,
738736

739737
flushSync: DOMRenderer.flushSync,

packages/react-reconciler/src/ReactFiberScheduler.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,11 +1568,14 @@ function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
15681568
function deferredUpdates<A>(fn: () => A): A {
15691569
const currentTime = requestCurrentTime();
15701570
const previousExpirationContext = expirationContext;
1571+
const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates;
15711572
expirationContext = computeAsyncExpiration(currentTime);
1573+
isBatchingInteractiveUpdates = false;
15721574
try {
15731575
return fn();
15741576
} finally {
15751577
expirationContext = previousExpirationContext;
1578+
isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates;
15761579
}
15771580
}
15781581

0 commit comments

Comments
 (0)