mirror of
https://github.com/zebrajr/react.git
synced 2025-12-07 00:20:28 +01:00
* Updates inside controlled events (onChange) are sync even in async mode This guarantees the DOM is in a consistent state before we yield back to the browser. We'll need to figure out a separate strategy for other interactive events. * Don't rely on flushing behavior of public batchedUpdates implementation Flush work as an explicit step at the end of the event, right before restoring controlled state. * Interactive updates At the beginning of an interactive browser event (events that fire as the result of a user interaction, like a click), check for pending updates that were scheduled in a previous interactive event. Flush the pending updates synchronously so that the event handlers are up-to-date before responding to the current event. We now have three classes of events: - Controlled events. Updates are always flushed synchronously. - Interactive events. Updates are async, unless another a subsequent event is fired before it can complete, as described above. They are also slightly higher priority than a normal async update. - Non-interactive events. These are treated as normal, low-priority async updates. * Flush lowest pending interactive update time Accounts for case when multiple interactive updates are scheduled at different priorities. This can happen when an interactive event is dispatched inside an async subtree, and there's an event handler on an ancestor that is outside the subtree. * Update comment about restoring controlled components
70 lines
2.2 KiB
JavaScript
70 lines
2.2 KiB
JavaScript
/**
|
|
* Copyright (c) 2013-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
import {
|
|
needsStateRestore,
|
|
restoreStateIfNeeded,
|
|
} from './ReactControlledComponent';
|
|
|
|
// Used as a way to call batchedUpdates when we don't have a reference to
|
|
// the renderer. Such as when we're dispatching events or if third party
|
|
// libraries need to call batchedUpdates. Eventually, this API will go away when
|
|
// everything is batched by default. We'll then have a similar API to opt-out of
|
|
// scheduled work and instead do synchronous work.
|
|
|
|
// Defaults
|
|
let _batchedUpdates = function(fn, bookkeeping) {
|
|
return fn(bookkeeping);
|
|
};
|
|
let _interactiveUpdates = function(fn, a, b) {
|
|
return fn(a, b);
|
|
};
|
|
let _flushInteractiveUpdates = function() {};
|
|
|
|
let isBatching = false;
|
|
export function batchedUpdates(fn, bookkeeping) {
|
|
if (isBatching) {
|
|
// If we are currently inside another batch, we need to wait until it
|
|
// fully completes before restoring state.
|
|
return fn(bookkeeping);
|
|
}
|
|
isBatching = true;
|
|
try {
|
|
return _batchedUpdates(fn, bookkeeping);
|
|
} finally {
|
|
// Here we wait until all updates have propagated, which is important
|
|
// when using controlled components within layers:
|
|
// https://github.com/facebook/react/issues/1698
|
|
// Then we restore state of any controlled component.
|
|
isBatching = false;
|
|
const controlledComponentsHavePendingUpdates = needsStateRestore();
|
|
if (controlledComponentsHavePendingUpdates) {
|
|
// If a controlled event was fired, we may need to restore the state of
|
|
// the DOM node back to the controlled value. This is necessary when React
|
|
// bails out of the update without touching the DOM.
|
|
_flushInteractiveUpdates();
|
|
restoreStateIfNeeded();
|
|
}
|
|
}
|
|
}
|
|
|
|
export function interactiveUpdates(fn, a, b) {
|
|
return _interactiveUpdates(fn, a, b);
|
|
}
|
|
|
|
export function flushInteractiveUpdates() {
|
|
return _flushInteractiveUpdates();
|
|
}
|
|
|
|
export const injection = {
|
|
injectRenderer(renderer) {
|
|
_batchedUpdates = renderer.batchedUpdates;
|
|
_interactiveUpdates = renderer.interactiveUpdates;
|
|
_flushInteractiveUpdates = renderer.flushInteractiveUpdates;
|
|
},
|
|
};
|