From a96efb5f41692ccacd6436c9cf8e12de440d206d Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 24 Aug 2018 18:17:04 +0100 Subject: [PATCH] Detect Fiber types to avoid inlining constants --- .../src/ReactSixteenAdapter.js | 40 ++++++------ .../src/detectFiberTags.js | 63 +++++++++++++++++++ 2 files changed, 81 insertions(+), 22 deletions(-) create mode 100644 packages/enzyme-adapter-react-16/src/detectFiberTags.js diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index 50aef74a7..ba4ab4b58 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -37,18 +37,10 @@ import { ensureKeyOrUndefined, } from 'enzyme-adapter-utils'; import findCurrentFiberUsingSlowPath from './findCurrentFiberUsingSlowPath'; +import detectFiberTags from './detectFiberTags'; -const HostRoot = 3; -const ClassComponent = 2; -const FragmentType = 10; -const FunctionalComponent = 1; -const HostPortal = 4; -const HostComponent = 5; -const HostText = 6; -const Mode = 11; -const ContextConsumerType = 12; -const ContextProviderType = 13; -const ForwardRefType = 14; +// Lazily populated if DOM is available. +let FiberTags = null; function nodeAndSiblingsArray(nodeWithSibling) { const array = []; @@ -115,9 +107,9 @@ function toTree(vnode) { // somewhere else. Should talk to sebastian about this perhaps const node = findCurrentFiberUsingSlowPath(vnode); switch (node.tag) { - case HostRoot: // 3 + case FiberTags.HostRoot: return childrenToTree(node.child); - case HostPortal: { // 4 + case FiberTags.HostPortal: { const { stateNode: { containerInfo }, memoizedProps: children, @@ -133,7 +125,7 @@ function toTree(vnode) { rendered: childrenToTree(node.child), }; } - case ClassComponent: + case FiberTags.ClassComponent: return { nodeType: 'class', type: node.type, @@ -143,7 +135,7 @@ function toTree(vnode) { instance: node.stateNode, rendered: childrenToTree(node.child), }; - case FunctionalComponent: // 1 + case FiberTags.FunctionalComponent: return { nodeType: 'function', type: node.type, @@ -154,7 +146,7 @@ function toTree(vnode) { rendered: childrenToTree(node.child), }; - case HostComponent: { // 5 + case FiberTags.HostComponent: { let renderedNodes = flatten(nodeAndSiblingsArray(node.child).map(toTree)); if (renderedNodes.length === 0) { renderedNodes = [node.memoizedProps.children]; @@ -169,14 +161,14 @@ function toTree(vnode) { rendered: renderedNodes, }; } - case HostText: // 6 + case FiberTags.HostText: return node.memoizedProps; - case FragmentType: // 10 - case Mode: // 11 - case ContextProviderType: // 13 - case ContextConsumerType: // 12 + case FiberTags.Fragment: + case FiberTags.Mode: + case FiberTags.ContextProvider: + case FiberTags.ContextConsumer: return childrenToTree(node.child); - case ForwardRefType: { + case FiberTags.ForwardRef: { return { nodeType: 'function', type: node.type, @@ -255,6 +247,10 @@ class ReactSixteenAdapter extends EnzymeAdapter { createMountRenderer(options) { assertDomAvailable('mount'); + if (FiberTags === null) { + // Requires DOM. + FiberTags = detectFiberTags(); + } const { attachTo, hydrateIn } = options; const domNode = hydrateIn || attachTo || global.document.createElement('div'); let instance = null; diff --git a/packages/enzyme-adapter-react-16/src/detectFiberTags.js b/packages/enzyme-adapter-react-16/src/detectFiberTags.js new file mode 100644 index 000000000..cffba524c --- /dev/null +++ b/packages/enzyme-adapter-react-16/src/detectFiberTags.js @@ -0,0 +1,63 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +function getFiber(element) { + const container = global.document.createElement('div'); + let inst = null; + class Tester extends React.Component { + render() { + inst = this; + return element; + } + } + ReactDOM.render(React.createElement(Tester), container); + return inst._reactInternalFiber.child; +} + +module.exports = function detectFiberTags() { + const supportsMode = typeof React.StrictMode !== 'undefined'; + const supportsContext = typeof React.createContext !== 'undefined'; + const supportsForwardRef = typeof React.forwardRef !== 'undefined'; + + function Fn() { + return null; + } + // eslint-disable-next-line react/prefer-stateless-function + class Cls extends React.Component { + render() { + return null; + } + } + let Ctx = null; + let FwdRef = null; + if (supportsContext) { + Ctx = React.createContext(); + } + if (supportsForwardRef) { + // React will warn if we don't have both arguments. + // eslint-disable-next-line no-unused-vars + FwdRef = React.forwardRef((props, ref) => null); + } + + return { + HostRoot: getFiber('test').return.return.tag, // Go two levels above to find the root + ClassComponent: getFiber(React.createElement(Cls)).tag, + Fragment: getFiber([['nested']]).tag, + FunctionalComponent: getFiber(React.createElement(Fn)).tag, + HostPortal: getFiber(ReactDOM.createPortal(null, global.document.createElement('div'))).tag, + HostComponent: getFiber(React.createElement('span')).tag, + HostText: getFiber('text').tag, + Mode: supportsMode + ? getFiber(React.createElement(React.StrictMode)).tag + : -1, + ContextConsumer: supportsContext + ? getFiber(React.createElement(Ctx.Consumer, null, () => null)).tag + : -1, + ContextProvider: supportsContext + ? getFiber(React.createElement(Ctx.Provider, { value: null })).tag + : -1, + ForwardRef: supportsForwardRef + ? getFiber(React.createElement(FwdRef)).tag + : -1, + }; +}