From 8dbf67894d1b760ba85eb9c1bcfa40ed53dbcf46 Mon Sep 17 00:00:00 2001 From: Dustan Kasten Date: Fri, 25 Aug 2017 14:42:31 -0600 Subject: [PATCH 1/2] Split out ReactART setup for Reconciler injection --- src/renderers/art/ReactARTFiberConfig.js | 553 +++++++++++++++++++++++ src/renderers/art/ReactARTFiberEntry.js | 542 +--------------------- 2 files changed, 556 insertions(+), 539 deletions(-) create mode 100644 src/renderers/art/ReactARTFiberConfig.js diff --git a/src/renderers/art/ReactARTFiberConfig.js b/src/renderers/art/ReactARTFiberConfig.js new file mode 100644 index 0000000000000..40e0b439b0868 --- /dev/null +++ b/src/renderers/art/ReactARTFiberConfig.js @@ -0,0 +1,553 @@ +/** + * Copyright (c) 2013-present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactARTFiberConfig + */ +'use strict'; + +require('art/modes/current').setCurrent( + // Change to 'art/modes/dom' for easier debugging via SVG + require('art/modes/fast-noSideEffects'), +); + +const Mode = require('art/modes/current'); +const Transform = require('art/core/transform'); +const invariant = require('fbjs/lib/invariant'); +const emptyObject = require('fbjs/lib/emptyObject'); +const React = require('react'); +const ReactDOMFrameScheduling = require('ReactDOMFrameScheduling'); + +const {Component} = React; + +const pooledTransform = new Transform(); + +const EVENT_TYPES = { + onClick: 'click', + onMouseMove: 'mousemove', + onMouseOver: 'mouseover', + onMouseOut: 'mouseout', + onMouseUp: 'mouseup', + onMouseDown: 'mousedown', +}; + +const TYPES = { + CLIPPING_RECTANGLE: 'ClippingRectangle', + GROUP: 'Group', + SHAPE: 'Shape', + TEXT: 'Text', +}; + +const UPDATE_SIGNAL = {}; + +/** Helper Methods */ + +function addEventListeners(instance, type, listener) { + // We need to explicitly unregister before unmount. + // For this reason we need to track subscriptions. + if (!instance._listeners) { + instance._listeners = {}; + instance._subscriptions = {}; + } + + instance._listeners[type] = listener; + + if (listener) { + if (!instance._subscriptions[type]) { + instance._subscriptions[type] = instance.subscribe( + type, + createEventHandler(instance), + instance, + ); + } + } else { + if (instance._subscriptions[type]) { + instance._subscriptions[type](); + delete instance._subscriptions[type]; + } + } +} + +function childrenAsString(children) { + if (!children) { + return ''; + } else if (typeof children === 'string') { + return children; + } else if (children.length) { + return children.join(''); + } else { + return ''; + } +} + +function createEventHandler(instance) { + return function handleEvent(event) { + const listener = instance._listeners[event.type]; + + if (!listener) { + // Noop + } else if (typeof listener === 'function') { + listener.call(instance, event); + } else if (listener.handleEvent) { + listener.handleEvent(event); + } + }; +} + +function destroyEventListeners(instance) { + if (instance._subscriptions) { + for (let type in instance._subscriptions) { + instance._subscriptions[type](); + } + } + + instance._subscriptions = null; + instance._listeners = null; +} + +function getScaleX(props) { + if (props.scaleX != null) { + return props.scaleX; + } else if (props.scale != null) { + return props.scale; + } else { + return 1; + } +} + +function getScaleY(props) { + if (props.scaleY != null) { + return props.scaleY; + } else if (props.scale != null) { + return props.scale; + } else { + return 1; + } +} + +function isSameFont(oldFont, newFont) { + if (oldFont === newFont) { + return true; + } else if (typeof newFont === 'string' || typeof oldFont === 'string') { + return false; + } else { + return ( + newFont.fontSize === oldFont.fontSize && + newFont.fontStyle === oldFont.fontStyle && + newFont.fontVariant === oldFont.fontVariant && + newFont.fontWeight === oldFont.fontWeight && + newFont.fontFamily === oldFont.fontFamily + ); + } +} + +/** Render Methods */ + +function applyClippingRectangleProps(instance, props, prevProps = {}) { + applyNodeProps(instance, props, prevProps); + + instance.width = props.width; + instance.height = props.height; +} + +function applyGroupProps(instance, props, prevProps = {}) { + applyNodeProps(instance, props, prevProps); + + instance.width = props.width; + instance.height = props.height; +} + +function applyNodeProps(instance, props, prevProps = {}) { + const scaleX = getScaleX(props); + const scaleY = getScaleY(props); + + pooledTransform + .transformTo(1, 0, 0, 1, 0, 0) + .move(props.x || 0, props.y || 0) + .rotate(props.rotation || 0, props.originX, props.originY) + .scale(scaleX, scaleY, props.originX, props.originY); + + if (props.transform != null) { + pooledTransform.transform(props.transform); + } + + if ( + instance.xx !== pooledTransform.xx || + instance.yx !== pooledTransform.yx || + instance.xy !== pooledTransform.xy || + instance.yy !== pooledTransform.yy || + instance.x !== pooledTransform.x || + instance.y !== pooledTransform.y + ) { + instance.transformTo(pooledTransform); + } + + if (props.cursor !== prevProps.cursor || props.title !== prevProps.title) { + instance.indicate(props.cursor, props.title); + } + + if (instance.blend && props.opacity !== prevProps.opacity) { + instance.blend(props.opacity == null ? 1 : props.opacity); + } + + if (props.visible !== prevProps.visible) { + if (props.visible == null || props.visible) { + instance.show(); + } else { + instance.hide(); + } + } + + for (let type in EVENT_TYPES) { + addEventListeners(instance, EVENT_TYPES[type], props[type]); + } +} + +function applyRenderableNodeProps(instance, props, prevProps = {}) { + applyNodeProps(instance, props, prevProps); + + if (prevProps.fill !== props.fill) { + if (props.fill && props.fill.applyFill) { + props.fill.applyFill(instance); + } else { + instance.fill(props.fill); + } + } + if ( + prevProps.stroke !== props.stroke || + prevProps.strokeWidth !== props.strokeWidth || + prevProps.strokeCap !== props.strokeCap || + prevProps.strokeJoin !== props.strokeJoin || + // TODO: Consider deep check of stokeDash; may benefit VML in IE. + prevProps.strokeDash !== props.strokeDash + ) { + instance.stroke( + props.stroke, + props.strokeWidth, + props.strokeCap, + props.strokeJoin, + props.strokeDash, + ); + } +} + +function applyShapeProps(instance, props, prevProps = {}) { + applyRenderableNodeProps(instance, props, prevProps); + + const path = props.d || childrenAsString(props.children); + + const prevDelta = instance._prevDelta; + const prevPath = instance._prevPath; + + if ( + path !== prevPath || + path.delta !== prevDelta || + prevProps.height !== props.height || + prevProps.width !== props.width + ) { + instance.draw(path, props.width, props.height); + + instance._prevDelta = path.delta; + instance._prevPath = path; + } +} + +function applyTextProps(instance, props, prevProps = {}) { + applyRenderableNodeProps(instance, props, prevProps); + + const string = props.children; + + if ( + instance._currentString !== string || + !isSameFont(props.font, prevProps.font) || + props.alignment !== prevProps.alignment || + props.path !== prevProps.path + ) { + instance.draw(string, props.font, props.alignment, props.path); + + instance._currentString = string; + } +} + +/** 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 Component { + componentDidMount() { + const {height, width} = this.props; + + this._surface = Mode.Surface(+width, +height, this._tagRef); + + this._mountNode = ARTRenderer.createContainer(this._surface); + ARTRenderer.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); + } + + ARTRenderer.updateContainer(this.props.children, this._mountNode, this); + + if (this._surface.render) { + this._surface.render(); + } + } + + componentWillUnmount() { + ARTRenderer.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 ( + (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 ( + (this._text = t)}> + {childrenAsString(this.props.children)} + + ); + } +} + +/** API */ + +let ARTRenderer; +module.exports = function(Reconciler) { + ARTRenderer = Reconciler({ + appendChild(parentInstance, child) { + if (child.parentNode === parentInstance) { + child.eject(); + } + child.inject(parentInstance); + }, + + appendChildToContainer(parentInstance, child) { + if (child.parentNode === parentInstance) { + child.eject(); + } + child.inject(parentInstance); + }, + + appendInitialChild(parentInstance, child) { + if (typeof child === 'string') { + // Noop for string children of Text (eg {'foo'}{'bar'}) + invariant(false, 'Text children should already be flattened.'); + return; + } + + child.inject(parentInstance); + }, + + commitTextUpdate(textInstance, oldText, newText) { + // Noop + }, + + commitMount(instance, type, newProps) { + // Noop + }, + + commitUpdate(instance, updatePayload, type, oldProps, newProps) { + instance._applyProps(instance, newProps, oldProps); + }, + + createInstance(type, props, internalInstanceHandle) { + let instance; + + switch (type) { + case TYPES.CLIPPING_RECTANGLE: + instance = Mode.ClippingRectangle(); + instance._applyProps = applyClippingRectangleProps; + break; + case TYPES.GROUP: + instance = Mode.Group(); + instance._applyProps = applyGroupProps; + break; + case TYPES.SHAPE: + instance = Mode.Shape(); + instance._applyProps = applyShapeProps; + break; + case TYPES.TEXT: + instance = Mode.Text( + props.children, + props.font, + props.alignment, + props.path, + ); + instance._applyProps = applyTextProps; + break; + } + + invariant(instance, 'ReactART does not support the type "%s"', type); + + instance._applyProps(instance, props); + + return instance; + }, + + createTextInstance(text, rootContainerInstance, internalInstanceHandle) { + return text; + }, + + finalizeInitialChildren(domElement, type, props) { + return false; + }, + + getPublicInstance(instance) { + return instance; + }, + + insertBefore(parentInstance, child, beforeChild) { + invariant( + child !== beforeChild, + 'ReactART: Can not insert node before itself', + ); + child.injectBefore(beforeChild); + }, + + insertInContainerBefore(parentInstance, child, beforeChild) { + invariant( + child !== beforeChild, + 'ReactART: Can not insert node before itself', + ); + child.injectBefore(beforeChild); + }, + + prepareForCommit() { + // Noop + }, + + prepareUpdate(domElement, type, oldProps, newProps) { + return UPDATE_SIGNAL; + }, + + removeChild(parentInstance, child) { + destroyEventListeners(child); + child.eject(); + }, + + removeChildFromContainer(parentInstance, child) { + destroyEventListeners(child); + child.eject(); + }, + + resetAfterCommit() { + // Noop + }, + + resetTextContent(domElement) { + // Noop + }, + + shouldDeprioritizeSubtree(type, props) { + return false; + }, + + getRootHostContext() { + return emptyObject; + }, + + getChildHostContext() { + return emptyObject; + }, + + scheduleDeferredCallback: ReactDOMFrameScheduling.rIC, + + shouldSetTextContent(type, props) { + return ( + typeof props.children === 'string' || typeof props.children === 'number' + ); + }, + + useSyncScheduling: true, + });; + + return { + ClippingRectangle: TYPES.CLIPPING_RECTANGLE, + Group: TYPES.GROUP, + LinearGradient, + Path: Mode.Path, + Pattern, + RadialGradient, + Shape: TYPES.SHAPE, + Surface, + Text: Text, + Transform, + }; +}; diff --git a/src/renderers/art/ReactARTFiberEntry.js b/src/renderers/art/ReactARTFiberEntry.js index 0f4086d6fab7d..e7d54b4ff9131 100644 --- a/src/renderers/art/ReactARTFiberEntry.js +++ b/src/renderers/art/ReactARTFiberEntry.js @@ -1,3 +1,4 @@ + /** * Copyright (c) 2013-present Facebook, Inc. * All rights reserved. @@ -10,544 +11,7 @@ */ 'use strict'; -require('art/modes/current').setCurrent( - // Change to 'art/modes/dom' for easier debugging via SVG - require('art/modes/fast-noSideEffects'), -); - -const Mode = require('art/modes/current'); -const Transform = require('art/core/transform'); -const invariant = require('fbjs/lib/invariant'); -const emptyObject = require('fbjs/lib/emptyObject'); -const React = require('react'); const ReactFiberReconciler = require('ReactFiberReconciler'); -const ReactDOMFrameScheduling = require('ReactDOMFrameScheduling'); - -const {Component} = React; - -const pooledTransform = new Transform(); - -const EVENT_TYPES = { - onClick: 'click', - onMouseMove: 'mousemove', - onMouseOver: 'mouseover', - onMouseOut: 'mouseout', - onMouseUp: 'mouseup', - onMouseDown: 'mousedown', -}; - -const TYPES = { - CLIPPING_RECTANGLE: 'ClippingRectangle', - GROUP: 'Group', - SHAPE: 'Shape', - TEXT: 'Text', -}; - -const UPDATE_SIGNAL = {}; - -/** Helper Methods */ - -function addEventListeners(instance, type, listener) { - // We need to explicitly unregister before unmount. - // For this reason we need to track subscriptions. - if (!instance._listeners) { - instance._listeners = {}; - instance._subscriptions = {}; - } - - instance._listeners[type] = listener; - - if (listener) { - if (!instance._subscriptions[type]) { - instance._subscriptions[type] = instance.subscribe( - type, - createEventHandler(instance), - instance, - ); - } - } else { - if (instance._subscriptions[type]) { - instance._subscriptions[type](); - delete instance._subscriptions[type]; - } - } -} - -function childrenAsString(children) { - if (!children) { - return ''; - } else if (typeof children === 'string') { - return children; - } else if (children.length) { - return children.join(''); - } else { - return ''; - } -} - -function createEventHandler(instance) { - return function handleEvent(event) { - const listener = instance._listeners[event.type]; - - if (!listener) { - // Noop - } else if (typeof listener === 'function') { - listener.call(instance, event); - } else if (listener.handleEvent) { - listener.handleEvent(event); - } - }; -} - -function destroyEventListeners(instance) { - if (instance._subscriptions) { - for (let type in instance._subscriptions) { - instance._subscriptions[type](); - } - } - - instance._subscriptions = null; - instance._listeners = null; -} - -function getScaleX(props) { - if (props.scaleX != null) { - return props.scaleX; - } else if (props.scale != null) { - return props.scale; - } else { - return 1; - } -} - -function getScaleY(props) { - if (props.scaleY != null) { - return props.scaleY; - } else if (props.scale != null) { - return props.scale; - } else { - return 1; - } -} - -function isSameFont(oldFont, newFont) { - if (oldFont === newFont) { - return true; - } else if (typeof newFont === 'string' || typeof oldFont === 'string') { - return false; - } else { - return ( - newFont.fontSize === oldFont.fontSize && - newFont.fontStyle === oldFont.fontStyle && - newFont.fontVariant === oldFont.fontVariant && - newFont.fontWeight === oldFont.fontWeight && - newFont.fontFamily === oldFont.fontFamily - ); - } -} - -/** Render Methods */ - -function applyClippingRectangleProps(instance, props, prevProps = {}) { - applyNodeProps(instance, props, prevProps); - - instance.width = props.width; - instance.height = props.height; -} - -function applyGroupProps(instance, props, prevProps = {}) { - applyNodeProps(instance, props, prevProps); - - instance.width = props.width; - instance.height = props.height; -} - -function applyNodeProps(instance, props, prevProps = {}) { - const scaleX = getScaleX(props); - const scaleY = getScaleY(props); - - pooledTransform - .transformTo(1, 0, 0, 1, 0, 0) - .move(props.x || 0, props.y || 0) - .rotate(props.rotation || 0, props.originX, props.originY) - .scale(scaleX, scaleY, props.originX, props.originY); - - if (props.transform != null) { - pooledTransform.transform(props.transform); - } - - if ( - instance.xx !== pooledTransform.xx || - instance.yx !== pooledTransform.yx || - instance.xy !== pooledTransform.xy || - instance.yy !== pooledTransform.yy || - instance.x !== pooledTransform.x || - instance.y !== pooledTransform.y - ) { - instance.transformTo(pooledTransform); - } - - if (props.cursor !== prevProps.cursor || props.title !== prevProps.title) { - instance.indicate(props.cursor, props.title); - } - - if (instance.blend && props.opacity !== prevProps.opacity) { - instance.blend(props.opacity == null ? 1 : props.opacity); - } - - if (props.visible !== prevProps.visible) { - if (props.visible == null || props.visible) { - instance.show(); - } else { - instance.hide(); - } - } - - for (let type in EVENT_TYPES) { - addEventListeners(instance, EVENT_TYPES[type], props[type]); - } -} - -function applyRenderableNodeProps(instance, props, prevProps = {}) { - applyNodeProps(instance, props, prevProps); - - if (prevProps.fill !== props.fill) { - if (props.fill && props.fill.applyFill) { - props.fill.applyFill(instance); - } else { - instance.fill(props.fill); - } - } - if ( - prevProps.stroke !== props.stroke || - prevProps.strokeWidth !== props.strokeWidth || - prevProps.strokeCap !== props.strokeCap || - prevProps.strokeJoin !== props.strokeJoin || - // TODO: Consider deep check of stokeDash; may benefit VML in IE. - prevProps.strokeDash !== props.strokeDash - ) { - instance.stroke( - props.stroke, - props.strokeWidth, - props.strokeCap, - props.strokeJoin, - props.strokeDash, - ); - } -} - -function applyShapeProps(instance, props, prevProps = {}) { - applyRenderableNodeProps(instance, props, prevProps); - - const path = props.d || childrenAsString(props.children); - - const prevDelta = instance._prevDelta; - const prevPath = instance._prevPath; - - if ( - path !== prevPath || - path.delta !== prevDelta || - prevProps.height !== props.height || - prevProps.width !== props.width - ) { - instance.draw(path, props.width, props.height); - - instance._prevDelta = path.delta; - instance._prevPath = path; - } -} - -function applyTextProps(instance, props, prevProps = {}) { - applyRenderableNodeProps(instance, props, prevProps); - - const string = props.children; - - if ( - instance._currentString !== string || - !isSameFont(props.font, prevProps.font) || - props.alignment !== prevProps.alignment || - props.path !== prevProps.path - ) { - instance.draw(string, props.font, props.alignment, props.path); - - instance._currentString = string; - } -} - -/** 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 Component { - componentDidMount() { - const {height, width} = this.props; - - this._surface = Mode.Surface(+width, +height, this._tagRef); - - this._mountNode = ARTRenderer.createContainer(this._surface); - ARTRenderer.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); - } - - ARTRenderer.updateContainer(this.props.children, this._mountNode, this); - - if (this._surface.render) { - this._surface.render(); - } - } - - componentWillUnmount() { - ARTRenderer.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 ( - (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 ( - (this._text = t)}> - {childrenAsString(this.props.children)} - - ); - } -} - -/** ART Renderer */ - -const ARTRenderer = ReactFiberReconciler({ - appendChild(parentInstance, child) { - if (child.parentNode === parentInstance) { - child.eject(); - } - child.inject(parentInstance); - }, - - appendChildToContainer(parentInstance, child) { - if (child.parentNode === parentInstance) { - child.eject(); - } - child.inject(parentInstance); - }, - - appendInitialChild(parentInstance, child) { - if (typeof child === 'string') { - // Noop for string children of Text (eg {'foo'}{'bar'}) - invariant(false, 'Text children should already be flattened.'); - return; - } - - child.inject(parentInstance); - }, - - commitTextUpdate(textInstance, oldText, newText) { - // Noop - }, - - commitMount(instance, type, newProps) { - // Noop - }, - - commitUpdate(instance, updatePayload, type, oldProps, newProps) { - instance._applyProps(instance, newProps, oldProps); - }, - - createInstance(type, props, internalInstanceHandle) { - let instance; - - switch (type) { - case TYPES.CLIPPING_RECTANGLE: - instance = Mode.ClippingRectangle(); - instance._applyProps = applyClippingRectangleProps; - break; - case TYPES.GROUP: - instance = Mode.Group(); - instance._applyProps = applyGroupProps; - break; - case TYPES.SHAPE: - instance = Mode.Shape(); - instance._applyProps = applyShapeProps; - break; - case TYPES.TEXT: - instance = Mode.Text( - props.children, - props.font, - props.alignment, - props.path, - ); - instance._applyProps = applyTextProps; - break; - } - - invariant(instance, 'ReactART does not support the type "%s"', type); - - instance._applyProps(instance, props); - - return instance; - }, - - createTextInstance(text, rootContainerInstance, internalInstanceHandle) { - return text; - }, - - finalizeInitialChildren(domElement, type, props) { - return false; - }, - - getPublicInstance(instance) { - return instance; - }, - - insertBefore(parentInstance, child, beforeChild) { - invariant( - child !== beforeChild, - 'ReactART: Can not insert node before itself', - ); - child.injectBefore(beforeChild); - }, - - insertInContainerBefore(parentInstance, child, beforeChild) { - invariant( - child !== beforeChild, - 'ReactART: Can not insert node before itself', - ); - child.injectBefore(beforeChild); - }, - - prepareForCommit() { - // Noop - }, - - prepareUpdate(domElement, type, oldProps, newProps) { - return UPDATE_SIGNAL; - }, - - removeChild(parentInstance, child) { - destroyEventListeners(child); - child.eject(); - }, - - removeChildFromContainer(parentInstance, child) { - destroyEventListeners(child); - child.eject(); - }, - - resetAfterCommit() { - // Noop - }, - - resetTextContent(domElement) { - // Noop - }, - - shouldDeprioritizeSubtree(type, props) { - return false; - }, - - getRootHostContext() { - return emptyObject; - }, - - getChildHostContext() { - return emptyObject; - }, - - scheduleDeferredCallback: ReactDOMFrameScheduling.rIC, - - shouldSetTextContent(type, props) { - return ( - typeof props.children === 'string' || typeof props.children === 'number' - ); - }, - - useSyncScheduling: true, -}); - -/** API */ +const ReactARTFiberConfig = require('ReactARTFiberConfig'); -module.exports = { - ClippingRectangle: TYPES.CLIPPING_RECTANGLE, - Group: TYPES.GROUP, - LinearGradient, - Path: Mode.Path, - Pattern, - RadialGradient, - Shape: TYPES.SHAPE, - Surface, - Text: Text, - Transform, -}; +module.exports = ReactARTFiberConfig(ReactFiberReconciler); From 75ed38603dfee7261e177265c8fa88710c6c287f Mon Sep 17 00:00:00 2001 From: Dustan Kasten Date: Fri, 25 Aug 2017 14:45:44 -0600 Subject: [PATCH 2/2] Split out ReactDOM setup for Reconciler injection --- .../dom/fiber/ReactDOMFiberConfig.js | 470 ++++++++++++++++++ src/renderers/dom/fiber/ReactDOMFiberEntry.js | 422 +--------------- 2 files changed, 472 insertions(+), 420 deletions(-) create mode 100644 src/renderers/dom/fiber/ReactDOMFiberConfig.js diff --git a/src/renderers/dom/fiber/ReactDOMFiberConfig.js b/src/renderers/dom/fiber/ReactDOMFiberConfig.js new file mode 100644 index 0000000000000..d7b247ee62617 --- /dev/null +++ b/src/renderers/dom/fiber/ReactDOMFiberConfig.js @@ -0,0 +1,470 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactDOMFiberConfig + * @flow + */ + +var DOMNamespaces = require('DOMNamespaces'); +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); +var ReactDOMComponentTree = require('ReactDOMComponentTree'); +var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); +var ReactDOMFiberComponent = require('ReactDOMFiberComponent'); +var ReactDOMFrameScheduling = require('ReactDOMFrameScheduling'); +var ReactInputSelection = require('ReactInputSelection'); +var { + ELEMENT_NODE, + TEXT_NODE, + COMMENT_NODE, + DOCUMENT_NODE, +} = require('HTMLNodeType'); + +if (__DEV__) { + var warning = require('fbjs/lib/warning'); + var validateDOMNesting = require('validateDOMNesting'); + var {updatedAncestorInfo} = validateDOMNesting; + + if ( + typeof Map !== 'function' || + Map.prototype == null || + typeof Map.prototype.forEach !== 'function' || + typeof Set !== 'function' || + Set.prototype == null || + typeof Set.prototype.clear !== 'function' || + typeof Set.prototype.forEach !== 'function' + ) { + warning( + false, + 'React depends on Map and Set built-in types. Make sure that you load a ' + + 'polyfill in older browsers. http://fb.me/react-polyfills', + ); + } +} + + +var {getChildNamespace} = DOMNamespaces; +var { + createElement, + setInitialProperties, + diffProperties, + updateProperties, + diffHydratedProperties, + diffHydratedText, + warnForDeletedHydratableElement, + warnForDeletedHydratableText, + warnForInsertedHydratedElement, + warnForInsertedHydratedText, +} = ReactDOMFiberComponent; +var {precacheFiberNode, updateFiberProps} = ReactDOMComponentTree; + +type Container = Element | Document; +type Props = { + autoFocus?: boolean, + children?: mixed, + hidden?: boolean, +}; +type Instance = Element; +type TextInstance = Text; + +type HostContextDev = { + namespace: string, + ancestorInfo: mixed, +}; +type HostContextProd = string; +type HostContext = HostContextDev | HostContextProd; + + +let eventsEnabled: ?boolean = null; +let selectionInformation: ?mixed = null; + +function shouldAutoFocusHostComponent(type: string, props: Props): boolean { + switch (type) { + case 'button': + case 'input': + case 'select': + case 'textarea': + return !!props.autoFocus; + } + return false; +} + +const ReactDOMFiberConfig = { + getRootHostContext(rootContainerInstance: Container): HostContext { + let type; + let namespace; + if (rootContainerInstance.nodeType === DOCUMENT_NODE) { + type = '#document'; + let root = (rootContainerInstance: any).documentElement; + namespace = root ? root.namespaceURI : getChildNamespace(null, ''); + } else { + const container: any = rootContainerInstance.nodeType === COMMENT_NODE + ? rootContainerInstance.parentNode + : rootContainerInstance; + const ownNamespace = container.namespaceURI || null; + type = container.tagName; + namespace = getChildNamespace(ownNamespace, type); + } + if (__DEV__) { + const validatedTag = type.toLowerCase(); + const ancestorInfo = updatedAncestorInfo(null, validatedTag, null); + return {namespace, ancestorInfo}; + } + return namespace; + }, + + getChildHostContext( + parentHostContext: HostContext, + type: string, + ): HostContext { + if (__DEV__) { + const parentHostContextDev = ((parentHostContext: any): HostContextDev); + const namespace = getChildNamespace(parentHostContextDev.namespace, type); + const ancestorInfo = updatedAncestorInfo( + parentHostContextDev.ancestorInfo, + type, + null, + ); + return {namespace, ancestorInfo}; + } + const parentNamespace = ((parentHostContext: any): HostContextProd); + return getChildNamespace(parentNamespace, type); + }, + + getPublicInstance(instance) { + return instance; + }, + + prepareForCommit(): void { + eventsEnabled = ReactBrowserEventEmitter.isEnabled(); + selectionInformation = ReactInputSelection.getSelectionInformation(); + ReactBrowserEventEmitter.setEnabled(false); + }, + + resetAfterCommit(): void { + ReactInputSelection.restoreSelection(selectionInformation); + selectionInformation = null; + ReactBrowserEventEmitter.setEnabled(eventsEnabled); + eventsEnabled = null; + }, + + createInstance( + type: string, + props: Props, + rootContainerInstance: Container, + hostContext: HostContext, + internalInstanceHandle: Object, + ): Instance { + let parentNamespace: string; + if (__DEV__) { + // TODO: take namespace into account when validating. + const hostContextDev = ((hostContext: any): HostContextDev); + validateDOMNesting(type, null, null, hostContextDev.ancestorInfo); + if ( + typeof props.children === 'string' || + typeof props.children === 'number' + ) { + const string = '' + props.children; + const ownAncestorInfo = updatedAncestorInfo( + hostContextDev.ancestorInfo, + type, + null, + ); + validateDOMNesting(null, string, null, ownAncestorInfo); + } + parentNamespace = hostContextDev.namespace; + } else { + parentNamespace = ((hostContext: any): HostContextProd); + } + const domElement: Instance = createElement( + type, + props, + rootContainerInstance, + parentNamespace, + ); + precacheFiberNode(internalInstanceHandle, domElement); + updateFiberProps(domElement, props); + return domElement; + }, + + appendInitialChild( + parentInstance: Instance, + child: Instance | TextInstance, + ): void { + parentInstance.appendChild(child); + }, + + finalizeInitialChildren( + domElement: Instance, + type: string, + props: Props, + rootContainerInstance: Container, + ): boolean { + setInitialProperties(domElement, type, props, rootContainerInstance); + return shouldAutoFocusHostComponent(type, props); + }, + + prepareUpdate( + domElement: Instance, + type: string, + oldProps: Props, + newProps: Props, + rootContainerInstance: Container, + hostContext: HostContext, + ): null | Array { + if (__DEV__) { + const hostContextDev = ((hostContext: any): HostContextDev); + if ( + typeof newProps.children !== typeof oldProps.children && + (typeof newProps.children === 'string' || + typeof newProps.children === 'number') + ) { + const string = '' + newProps.children; + const ownAncestorInfo = updatedAncestorInfo( + hostContextDev.ancestorInfo, + type, + null, + ); + validateDOMNesting(null, string, null, ownAncestorInfo); + } + } + return diffProperties( + domElement, + type, + oldProps, + newProps, + rootContainerInstance, + ); + }, + + commitMount( + domElement: Instance, + type: string, + newProps: Props, + internalInstanceHandle: Object, + ): void { + ((domElement: any): + | HTMLButtonElement + | HTMLInputElement + | HTMLSelectElement + | HTMLTextAreaElement).focus(); + }, + + commitUpdate( + domElement: Instance, + updatePayload: Array, + type: string, + oldProps: Props, + newProps: Props, + internalInstanceHandle: Object, + ): void { + // Update the props handle so that we know which props are the ones with + // with current event handlers. + updateFiberProps(domElement, newProps); + // Apply the diff to the DOM node. + updateProperties(domElement, updatePayload, type, oldProps, newProps); + }, + + shouldSetTextContent(type: string, props: Props): boolean { + return ( + type === 'textarea' || + typeof props.children === 'string' || + typeof props.children === 'number' || + (typeof props.dangerouslySetInnerHTML === 'object' && + props.dangerouslySetInnerHTML !== null && + typeof props.dangerouslySetInnerHTML.__html === 'string') + ); + }, + + resetTextContent(domElement: Instance): void { + domElement.textContent = ''; + }, + + shouldDeprioritizeSubtree(type: string, props: Props): boolean { + return !!props.hidden; + }, + + createTextInstance( + text: string, + rootContainerInstance: Container, + hostContext: HostContext, + internalInstanceHandle: Object, + ): TextInstance { + if (__DEV__) { + const hostContextDev = ((hostContext: any): HostContextDev); + validateDOMNesting(null, text, null, hostContextDev.ancestorInfo); + } + var textNode: TextInstance = document.createTextNode(text); + precacheFiberNode(internalInstanceHandle, textNode); + return textNode; + }, + + commitTextUpdate( + textInstance: TextInstance, + oldText: string, + newText: string, + ): void { + textInstance.nodeValue = newText; + }, + + appendChild(parentInstance: Instance, child: Instance | TextInstance): void { + parentInstance.appendChild(child); + }, + + appendChildToContainer( + container: Container, + child: Instance | TextInstance, + ): void { + if (container.nodeType === COMMENT_NODE) { + (container.parentNode: any).insertBefore(child, container); + } else { + container.appendChild(child); + } + }, + + insertBefore( + parentInstance: Instance, + child: Instance | TextInstance, + beforeChild: Instance | TextInstance, + ): void { + parentInstance.insertBefore(child, beforeChild); + }, + + insertInContainerBefore( + container: Container, + child: Instance | TextInstance, + beforeChild: Instance | TextInstance, + ): void { + if (container.nodeType === COMMENT_NODE) { + (container.parentNode: any).insertBefore(child, beforeChild); + } else { + container.insertBefore(child, beforeChild); + } + }, + + removeChild(parentInstance: Instance, child: Instance | TextInstance): void { + parentInstance.removeChild(child); + }, + + removeChildFromContainer( + container: Container, + child: Instance | TextInstance, + ): void { + if (container.nodeType === COMMENT_NODE) { + (container.parentNode: any).removeChild(child); + } else { + container.removeChild(child); + } + }, + + canHydrateInstance( + instance: Instance | TextInstance, + type: string, + props: Props, + ): boolean { + return ( + instance.nodeType === ELEMENT_NODE && + type === instance.nodeName.toLowerCase() + ); + }, + + canHydrateTextInstance( + instance: Instance | TextInstance, + text: string, + ): boolean { + if (text === '') { + // Empty strings are not parsed by HTML so there won't be a correct match here. + return false; + } + return instance.nodeType === TEXT_NODE; + }, + + getNextHydratableSibling( + instance: Instance | TextInstance, + ): null | Instance | TextInstance { + let node = instance.nextSibling; + // Skip non-hydratable nodes. + while ( + node && + node.nodeType !== ELEMENT_NODE && + node.nodeType !== TEXT_NODE + ) { + node = node.nextSibling; + } + return (node: any); + }, + + getFirstHydratableChild( + parentInstance: Container | Instance, + ): null | Instance | TextInstance { + let next = parentInstance.firstChild; + // Skip non-hydratable nodes. + while ( + next && + next.nodeType !== ELEMENT_NODE && + next.nodeType !== TEXT_NODE + ) { + next = next.nextSibling; + } + return (next: any); + }, + + hydrateInstance( + instance: Instance, + type: string, + props: Props, + rootContainerInstance: Container, + internalInstanceHandle: Object, + ): null | Array { + precacheFiberNode(internalInstanceHandle, instance); + // TODO: Possibly defer this until the commit phase where all the events + // get attached. + updateFiberProps(instance, props); + return diffHydratedProperties(instance, type, props, rootContainerInstance); + }, + + hydrateTextInstance( + textInstance: TextInstance, + text: string, + internalInstanceHandle: Object, + ): boolean { + precacheFiberNode(internalInstanceHandle, textInstance); + return diffHydratedText(textInstance, text); + }, + + didNotHydrateInstance( + parentInstance: Instance | Container, + instance: Instance | TextInstance, + ) { + if (instance.nodeType === 1) { + warnForDeletedHydratableElement(parentInstance, (instance: any)); + } else { + warnForDeletedHydratableText(parentInstance, (instance: any)); + } + }, + + didNotFindHydratableInstance( + parentInstance: Instance | Container, + type: string, + props: Props, + ) { + warnForInsertedHydratedElement(parentInstance, type, props); + }, + + didNotFindHydratableTextInstance( + parentInstance: Instance | Container, + text: string, + ) { + warnForInsertedHydratedText(parentInstance, text); + }, + + scheduleDeferredCallback: ReactDOMFrameScheduling.rIC, + + useSyncScheduling: !ReactDOMFeatureFlags.fiberAsyncScheduling, +}; + +module.exports = ReactDOMFiberConfig; diff --git a/src/renderers/dom/fiber/ReactDOMFiberEntry.js b/src/renderers/dom/fiber/ReactDOMFiberEntry.js index e7a2594712125..bb1eb5756f6ac 100644 --- a/src/renderers/dom/fiber/ReactDOMFiberEntry.js +++ b/src/renderers/dom/fiber/ReactDOMFiberEntry.js @@ -16,18 +16,14 @@ import type {Fiber} from 'ReactFiber'; import type {ReactNodeList} from 'ReactTypes'; require('checkReact'); -var DOMNamespaces = require('DOMNamespaces'); var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment'); -var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactControlledComponent = require('ReactControlledComponent'); var ReactDOMComponentTree = require('ReactDOMComponentTree'); var ReactFeatureFlags = require('ReactFeatureFlags'); -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); var ReactDOMFiberComponent = require('ReactDOMFiberComponent'); -var ReactDOMFrameScheduling = require('ReactDOMFrameScheduling'); var ReactGenericBatching = require('ReactGenericBatching'); var ReactFiberReconciler = require('ReactFiberReconciler'); -var ReactInputSelection = require('ReactInputSelection'); +var ReactDOMFiberConfig = require('ReactDOMFiberConfig'); var ReactInstanceMap = require('ReactInstanceMap'); var ReactPortal = require('ReactPortal'); var ReactVersion = require('ReactVersion'); @@ -35,7 +31,6 @@ var {isValidElement} = require('react'); var {injectInternals} = require('ReactFiberDevToolsHook'); var { ELEMENT_NODE, - TEXT_NODE, COMMENT_NODE, DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE, @@ -45,26 +40,9 @@ var {ROOT_ATTRIBUTE_NAME} = require('DOMProperty'); var findDOMNode = require('findDOMNode'); var invariant = require('fbjs/lib/invariant'); -var {getChildNamespace} = DOMNamespaces; -var { - createElement, - setInitialProperties, - diffProperties, - updateProperties, - diffHydratedProperties, - diffHydratedText, - warnForDeletedHydratableElement, - warnForDeletedHydratableText, - warnForInsertedHydratedElement, - warnForInsertedHydratedText, -} = ReactDOMFiberComponent; -var {precacheFiberNode, updateFiberProps} = ReactDOMComponentTree; - if (__DEV__) { var lowPriorityWarning = require('lowPriorityWarning'); var warning = require('fbjs/lib/warning'); - var validateDOMNesting = require('validateDOMNesting'); - var {updatedAncestorInfo} = validateDOMNesting; if ( typeof Map !== 'function' || @@ -100,24 +78,11 @@ type DOMContainer = _reactRootContainer: ?Object, }); -type Container = Element | Document; type Props = { autoFocus?: boolean, children?: mixed, hidden?: boolean, }; -type Instance = Element; -type TextInstance = Text; - -type HostContextDev = { - namespace: string, - ancestorInfo: mixed, -}; -type HostContextProd = string; -type HostContext = HostContextDev | HostContextProd; - -let eventsEnabled: ?boolean = null; -let selectionInformation: ?mixed = null; /** * True if the supplied DOM node is a valid node element. @@ -154,390 +119,7 @@ function shouldHydrateDueToLegacyHeuristic(container) { rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME)); } -function shouldAutoFocusHostComponent(type: string, props: Props): boolean { - switch (type) { - case 'button': - case 'input': - case 'select': - case 'textarea': - return !!props.autoFocus; - } - return false; -} - -var DOMRenderer = ReactFiberReconciler({ - getRootHostContext(rootContainerInstance: Container): HostContext { - let type; - let namespace; - if (rootContainerInstance.nodeType === DOCUMENT_NODE) { - type = '#document'; - let root = (rootContainerInstance: any).documentElement; - namespace = root ? root.namespaceURI : getChildNamespace(null, ''); - } else { - const container: any = rootContainerInstance.nodeType === COMMENT_NODE - ? rootContainerInstance.parentNode - : rootContainerInstance; - const ownNamespace = container.namespaceURI || null; - type = container.tagName; - namespace = getChildNamespace(ownNamespace, type); - } - if (__DEV__) { - const validatedTag = type.toLowerCase(); - const ancestorInfo = updatedAncestorInfo(null, validatedTag, null); - return {namespace, ancestorInfo}; - } - return namespace; - }, - - getChildHostContext( - parentHostContext: HostContext, - type: string, - ): HostContext { - if (__DEV__) { - const parentHostContextDev = ((parentHostContext: any): HostContextDev); - const namespace = getChildNamespace(parentHostContextDev.namespace, type); - const ancestorInfo = updatedAncestorInfo( - parentHostContextDev.ancestorInfo, - type, - null, - ); - return {namespace, ancestorInfo}; - } - const parentNamespace = ((parentHostContext: any): HostContextProd); - return getChildNamespace(parentNamespace, type); - }, - - getPublicInstance(instance) { - return instance; - }, - - prepareForCommit(): void { - eventsEnabled = ReactBrowserEventEmitter.isEnabled(); - selectionInformation = ReactInputSelection.getSelectionInformation(); - ReactBrowserEventEmitter.setEnabled(false); - }, - - resetAfterCommit(): void { - ReactInputSelection.restoreSelection(selectionInformation); - selectionInformation = null; - ReactBrowserEventEmitter.setEnabled(eventsEnabled); - eventsEnabled = null; - }, - - createInstance( - type: string, - props: Props, - rootContainerInstance: Container, - hostContext: HostContext, - internalInstanceHandle: Object, - ): Instance { - let parentNamespace: string; - if (__DEV__) { - // TODO: take namespace into account when validating. - const hostContextDev = ((hostContext: any): HostContextDev); - validateDOMNesting(type, null, null, hostContextDev.ancestorInfo); - if ( - typeof props.children === 'string' || - typeof props.children === 'number' - ) { - const string = '' + props.children; - const ownAncestorInfo = updatedAncestorInfo( - hostContextDev.ancestorInfo, - type, - null, - ); - validateDOMNesting(null, string, null, ownAncestorInfo); - } - parentNamespace = hostContextDev.namespace; - } else { - parentNamespace = ((hostContext: any): HostContextProd); - } - const domElement: Instance = createElement( - type, - props, - rootContainerInstance, - parentNamespace, - ); - precacheFiberNode(internalInstanceHandle, domElement); - updateFiberProps(domElement, props); - return domElement; - }, - - appendInitialChild( - parentInstance: Instance, - child: Instance | TextInstance, - ): void { - parentInstance.appendChild(child); - }, - - finalizeInitialChildren( - domElement: Instance, - type: string, - props: Props, - rootContainerInstance: Container, - ): boolean { - setInitialProperties(domElement, type, props, rootContainerInstance); - return shouldAutoFocusHostComponent(type, props); - }, - - prepareUpdate( - domElement: Instance, - type: string, - oldProps: Props, - newProps: Props, - rootContainerInstance: Container, - hostContext: HostContext, - ): null | Array { - if (__DEV__) { - const hostContextDev = ((hostContext: any): HostContextDev); - if ( - typeof newProps.children !== typeof oldProps.children && - (typeof newProps.children === 'string' || - typeof newProps.children === 'number') - ) { - const string = '' + newProps.children; - const ownAncestorInfo = updatedAncestorInfo( - hostContextDev.ancestorInfo, - type, - null, - ); - validateDOMNesting(null, string, null, ownAncestorInfo); - } - } - return diffProperties( - domElement, - type, - oldProps, - newProps, - rootContainerInstance, - ); - }, - - commitMount( - domElement: Instance, - type: string, - newProps: Props, - internalInstanceHandle: Object, - ): void { - ((domElement: any): - | HTMLButtonElement - | HTMLInputElement - | HTMLSelectElement - | HTMLTextAreaElement).focus(); - }, - - commitUpdate( - domElement: Instance, - updatePayload: Array, - type: string, - oldProps: Props, - newProps: Props, - internalInstanceHandle: Object, - ): void { - // Update the props handle so that we know which props are the ones with - // with current event handlers. - updateFiberProps(domElement, newProps); - // Apply the diff to the DOM node. - updateProperties(domElement, updatePayload, type, oldProps, newProps); - }, - - shouldSetTextContent(type: string, props: Props): boolean { - return ( - type === 'textarea' || - typeof props.children === 'string' || - typeof props.children === 'number' || - (typeof props.dangerouslySetInnerHTML === 'object' && - props.dangerouslySetInnerHTML !== null && - typeof props.dangerouslySetInnerHTML.__html === 'string') - ); - }, - - resetTextContent(domElement: Instance): void { - domElement.textContent = ''; - }, - - shouldDeprioritizeSubtree(type: string, props: Props): boolean { - return !!props.hidden; - }, - - createTextInstance( - text: string, - rootContainerInstance: Container, - hostContext: HostContext, - internalInstanceHandle: Object, - ): TextInstance { - if (__DEV__) { - const hostContextDev = ((hostContext: any): HostContextDev); - validateDOMNesting(null, text, null, hostContextDev.ancestorInfo); - } - var textNode: TextInstance = document.createTextNode(text); - precacheFiberNode(internalInstanceHandle, textNode); - return textNode; - }, - - commitTextUpdate( - textInstance: TextInstance, - oldText: string, - newText: string, - ): void { - textInstance.nodeValue = newText; - }, - - appendChild(parentInstance: Instance, child: Instance | TextInstance): void { - parentInstance.appendChild(child); - }, - - appendChildToContainer( - container: Container, - child: Instance | TextInstance, - ): void { - if (container.nodeType === COMMENT_NODE) { - (container.parentNode: any).insertBefore(child, container); - } else { - container.appendChild(child); - } - }, - - insertBefore( - parentInstance: Instance, - child: Instance | TextInstance, - beforeChild: Instance | TextInstance, - ): void { - parentInstance.insertBefore(child, beforeChild); - }, - - insertInContainerBefore( - container: Container, - child: Instance | TextInstance, - beforeChild: Instance | TextInstance, - ): void { - if (container.nodeType === COMMENT_NODE) { - (container.parentNode: any).insertBefore(child, beforeChild); - } else { - container.insertBefore(child, beforeChild); - } - }, - - removeChild(parentInstance: Instance, child: Instance | TextInstance): void { - parentInstance.removeChild(child); - }, - - removeChildFromContainer( - container: Container, - child: Instance | TextInstance, - ): void { - if (container.nodeType === COMMENT_NODE) { - (container.parentNode: any).removeChild(child); - } else { - container.removeChild(child); - } - }, - - canHydrateInstance( - instance: Instance | TextInstance, - type: string, - props: Props, - ): boolean { - return ( - instance.nodeType === ELEMENT_NODE && - type === instance.nodeName.toLowerCase() - ); - }, - - canHydrateTextInstance( - instance: Instance | TextInstance, - text: string, - ): boolean { - if (text === '') { - // Empty strings are not parsed by HTML so there won't be a correct match here. - return false; - } - return instance.nodeType === TEXT_NODE; - }, - - getNextHydratableSibling( - instance: Instance | TextInstance, - ): null | Instance | TextInstance { - let node = instance.nextSibling; - // Skip non-hydratable nodes. - while ( - node && - node.nodeType !== ELEMENT_NODE && - node.nodeType !== TEXT_NODE - ) { - node = node.nextSibling; - } - return (node: any); - }, - - getFirstHydratableChild( - parentInstance: Container | Instance, - ): null | Instance | TextInstance { - let next = parentInstance.firstChild; - // Skip non-hydratable nodes. - while ( - next && - next.nodeType !== ELEMENT_NODE && - next.nodeType !== TEXT_NODE - ) { - next = next.nextSibling; - } - return (next: any); - }, - - hydrateInstance( - instance: Instance, - type: string, - props: Props, - rootContainerInstance: Container, - internalInstanceHandle: Object, - ): null | Array { - precacheFiberNode(internalInstanceHandle, instance); - // TODO: Possibly defer this until the commit phase where all the events - // get attached. - updateFiberProps(instance, props); - return diffHydratedProperties(instance, type, props, rootContainerInstance); - }, - - hydrateTextInstance( - textInstance: TextInstance, - text: string, - internalInstanceHandle: Object, - ): boolean { - precacheFiberNode(internalInstanceHandle, textInstance); - return diffHydratedText(textInstance, text); - }, - - didNotHydrateInstance( - parentInstance: Instance | Container, - instance: Instance | TextInstance, - ) { - if (instance.nodeType === 1) { - warnForDeletedHydratableElement(parentInstance, (instance: any)); - } else { - warnForDeletedHydratableText(parentInstance, (instance: any)); - } - }, - - didNotFindHydratableInstance( - parentInstance: Instance | Container, - type: string, - props: Props, - ) { - warnForInsertedHydratedElement(parentInstance, type, props); - }, - - didNotFindHydratableTextInstance( - parentInstance: Instance | Container, - text: string, - ) { - warnForInsertedHydratedText(parentInstance, text); - }, - - scheduleDeferredCallback: ReactDOMFrameScheduling.rIC, - - useSyncScheduling: !ReactDOMFeatureFlags.fiberAsyncScheduling, -}); +var DOMRenderer = ReactDOMFiberConfig(ReactFiberReconciler); ReactGenericBatching.injection.injectFiberBatchedUpdates( DOMRenderer.batchedUpdates,