Skip to content
This repository was archived by the owner on Feb 6, 2023. It is now read-only.

Commit cda13cb

Browse files
acdlitefacebook-github-bot
authored andcommitted
Wrap Draft handlers in flushControlled instead of flushSync
Summary: In D6727557, we tried wrapping all Draft event handlers in `ReactDOM.flushSync`. The purpose was to ensure that updates scheduled inside event handlers would flush before React yields back to the browser. (This is only relevant to async mode, since in sync mode everything is sync by default.) The problem was that `flushSync` contains an invariant that throws if it is called inside a lifecycle method, to protect against reentrant React renders, which we intentionally do not support. What's nice about this invariant is that it guarantees all the work is flushed by the time the function returns. Usually this is fine, because event handlers are at the top of the stack. But there are cases where an event is dispatched from inside a lifecycle, like `el.focus`. The fix is to switch to a new lifecycle method, `unstable_flushControlled`. This is similar to `flushSync`, but it will not throw if called inside a lifecycle; instead, the work will be deferred and flushed at the end of the current batch. This still satisfies Draft's requirement that the work is flushed before React yields to the browser. `flushSync` is still the preferred API for most cases, but what we've learned from this is that it's only safe if you can be sure it's not nested inside other React work. E.g. calling it from inside a `requestAnimationFrame` callback is perfectly safe because you know you're at the top of the stack. If there's any indirection, especially related to DOM events, you likely want `flushControlled` instead. Reviewed By: sophiebits Differential Revision: D6861819 fbshipit-source-id: f5d4346fdff634ddaba5955db08c0c5a1b41cd0e
1 parent 35b3605 commit cda13cb

File tree

1 file changed

+10
-5
lines changed

1 file changed

+10
-5
lines changed

src/component/base/DraftEditor.react.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,19 @@ class DraftEditor extends React.Component<DraftEditorProps, State> {
175175
* editor mode, if any has been specified.
176176
*/
177177
_buildHandler(eventName: string): Function {
178-
const flushSyncSupported = !!ReactDOM.flushSync;
178+
const flushControlled = ReactDOM.unstable_flushControlled;
179+
// Wrap event handlers in `flushControlled`. In sync mode, this is
180+
// effetively a no-op. In async mode, this ensures all updates scheduled
181+
// inside the handler are flushed before React yields to the browser.
179182
return e => {
180183
if (!this.props.readOnly) {
181184
const method = this._handler && this._handler[eventName];
182-
if (flushSyncSupported && gkx('draft_js_flush_sync')) {
183-
method && ReactDOM.flushSync(() => method(this, e));
184-
} else {
185-
method && method(this, e);
185+
if (method) {
186+
if (flushControlled && gkx('draft_js_flush_sync')) {
187+
flushControlled(() => method(this, e));
188+
} else {
189+
method(this, e);
190+
}
186191
}
187192
}
188193
};

0 commit comments

Comments
 (0)