mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
* Add Batched Mode React has an unfortunate quirk where updates are sometimes synchronous -- where React starts rendering immediately within the call stack of `setState` — and sometimes batched, where updates are flushed at the end of the current event. Any update that originates within the call stack of the React event system is batched. This encompasses most updates, since most updates originate from an event handler like `onClick` or `onChange`. It also includes updates triggered by lifecycle methods or effects. But there are also updates that originate outside React's event system, like timer events, network events, and microtasks (promise resolution handlers). These are not batched, which results in both worse performance (multiple render passes instead of single one) and confusing semantics. Ideally all updates would be batched by default. Unfortunately, it's easy for components to accidentally rely on this behavior, so changing it could break existing apps in subtle ways. One way to move to a batched-by-default model is to opt into Concurrent Mode (still experimental). But Concurrent Mode introduces additional semantic changes that apps may not be ready to adopt. This commit introduces an additional mode called Batched Mode. Batched Mode enables a batched-by-default model that defers all updates to the next React event. Once it begins rendering, React will not yield to the browser until the entire render is finished. Batched Mode is superset of Strict Mode. It fires all the same warnings. It also drops the forked Suspense behavior used by Legacy Mode, in favor of the proper semantics used by Concurrent Mode. I have not added any public APIs that expose the new mode yet. I'll do that in subsequent commits. * Suspense in Batched Mode Should have same semantics as Concurrent Mode. * Use RootTag field to configure type of root There are three types of roots: Legacy, Batched, and Concurrent. * flushSync should not flush batched work Treat Sync and Batched expiration times separately. Only Sync updates are pushed to our internal queue of synchronous callbacks. Renamed `flushImmediateQueue` to `flushSyncCallbackQueue` for clarity.
154 lines
3.8 KiB
JavaScript
154 lines
3.8 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
import React from 'react';
|
|
import ReactVersion from 'shared/ReactVersion';
|
|
import {LegacyRoot} from 'shared/ReactRootTags';
|
|
import {
|
|
createContainer,
|
|
updateContainer,
|
|
injectIntoDevTools,
|
|
} from 'react-reconciler/inline.art';
|
|
import Transform from 'art/core/transform';
|
|
import Mode from 'art/modes/current';
|
|
import FastNoSideEffects from 'art/modes/fast-noSideEffects';
|
|
|
|
import {TYPES, childrenAsString} from './ReactARTInternals';
|
|
|
|
Mode.setCurrent(
|
|
// Change to 'art/modes/dom' for easier debugging via SVG
|
|
FastNoSideEffects,
|
|
);
|
|
|
|
/** Declarative fill-type objects; API design not finalized */
|
|
|
|
const slice = Array.prototype.slice;
|
|
|
|
class LinearGradient {
|
|
constructor(stops, x1, y1, x2, y2) {
|
|
this._args = slice.call(arguments);
|
|
}
|
|
|
|
applyFill(node) {
|
|
node.fillLinear.apply(node, this._args);
|
|
}
|
|
}
|
|
|
|
class RadialGradient {
|
|
constructor(stops, fx, fy, rx, ry, cx, cy) {
|
|
this._args = slice.call(arguments);
|
|
}
|
|
|
|
applyFill(node) {
|
|
node.fillRadial.apply(node, this._args);
|
|
}
|
|
}
|
|
|
|
class Pattern {
|
|
constructor(url, width, height, left, top) {
|
|
this._args = slice.call(arguments);
|
|
}
|
|
|
|
applyFill(node) {
|
|
node.fillImage.apply(node, this._args);
|
|
}
|
|
}
|
|
|
|
/** React Components */
|
|
|
|
class Surface extends React.Component {
|
|
componentDidMount() {
|
|
const {height, width} = this.props;
|
|
|
|
this._surface = Mode.Surface(+width, +height, this._tagRef);
|
|
|
|
this._mountNode = createContainer(this._surface, LegacyRoot, false);
|
|
updateContainer(this.props.children, this._mountNode, this);
|
|
}
|
|
|
|
componentDidUpdate(prevProps, prevState) {
|
|
const props = this.props;
|
|
|
|
if (props.height !== prevProps.height || props.width !== prevProps.width) {
|
|
this._surface.resize(+props.width, +props.height);
|
|
}
|
|
|
|
updateContainer(this.props.children, this._mountNode, this);
|
|
|
|
if (this._surface.render) {
|
|
this._surface.render();
|
|
}
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
updateContainer(null, this._mountNode, this);
|
|
}
|
|
|
|
render() {
|
|
// This is going to be a placeholder because we don't know what it will
|
|
// actually resolve to because ART may render canvas, vml or svg tags here.
|
|
// We only allow a subset of properties since others might conflict with
|
|
// ART's properties.
|
|
const props = this.props;
|
|
|
|
// TODO: ART's Canvas Mode overrides surface title and cursor
|
|
const Tag = Mode.Surface.tagName;
|
|
|
|
return (
|
|
<Tag
|
|
ref={ref => (this._tagRef = ref)}
|
|
accessKey={props.accessKey}
|
|
className={props.className}
|
|
draggable={props.draggable}
|
|
role={props.role}
|
|
style={props.style}
|
|
tabIndex={props.tabIndex}
|
|
title={props.title}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
class Text extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
// We allow reading these props. Ideally we could expose the Text node as
|
|
// ref directly.
|
|
['height', 'width', 'x', 'y'].forEach(key => {
|
|
Object.defineProperty(this, key, {
|
|
get: function() {
|
|
return this._text ? this._text[key] : undefined;
|
|
},
|
|
});
|
|
});
|
|
}
|
|
render() {
|
|
// This means you can't have children that render into strings...
|
|
const T = TYPES.TEXT;
|
|
return (
|
|
<T {...this.props} ref={t => (this._text = t)}>
|
|
{childrenAsString(this.props.children)}
|
|
</T>
|
|
);
|
|
}
|
|
}
|
|
|
|
injectIntoDevTools({
|
|
findFiberByHostInstance: () => null,
|
|
bundleType: __DEV__ ? 1 : 0,
|
|
version: ReactVersion,
|
|
rendererPackageName: 'react-art',
|
|
});
|
|
|
|
/** API */
|
|
|
|
export const ClippingRectangle = TYPES.CLIPPING_RECTANGLE;
|
|
export const Group = TYPES.GROUP;
|
|
export const Shape = TYPES.SHAPE;
|
|
export const Path = Mode.Path;
|
|
export {LinearGradient, Pattern, RadialGradient, Surface, Text, Transform};
|