From 4175f05934d3954827c00b7633c1ce8e4e87d227 Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 19 Apr 2022 17:34:49 -0400 Subject: [PATCH] Temporarily feature flag numeric fallback for symbols (#24401) --- .../react/src/__tests__/ReactElement-test.js | 112 +++++++++++++++++ .../src/__tests__/ReactElementJSX-test.js | 113 ++++++++++++++++++ packages/shared/ReactFeatureFlags.js | 3 + packages/shared/ReactSymbols.js | 89 ++++++++++---- .../__tests__/ReactSymbols-test.internal.js | 16 +++ .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + .../ReactFeatureFlags.test-renderer.native.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.testing.js | 1 + .../forks/ReactFeatureFlags.testing.www.js | 1 + .../forks/ReactFeatureFlags.www-dynamic.js | 2 +- .../shared/forks/ReactFeatureFlags.www.js | 1 + packages/shared/isValidElementType.js | 12 +- 15 files changed, 328 insertions(+), 27 deletions(-) diff --git a/packages/react/src/__tests__/ReactElement-test.js b/packages/react/src/__tests__/ReactElement-test.js index cc14e172ef6e5..95499947b80fc 100644 --- a/packages/react/src/__tests__/ReactElement-test.js +++ b/packages/react/src/__tests__/ReactElement-test.js @@ -9,16 +9,26 @@ 'use strict'; +import {enableSymbolFallbackForWWW} from 'shared/ReactFeatureFlags'; + let React; let ReactDOM; let ReactTestUtils; describe('ReactElement', () => { let ComponentClass; + let originalSymbol; beforeEach(() => { jest.resetModules(); + if (enableSymbolFallbackForWWW) { + // Delete the native Symbol if we have one to ensure we test the + // unpolyfilled environment. + originalSymbol = global.Symbol; + global.Symbol = undefined; + } + React = require('react'); ReactDOM = require('react-dom'); ReactTestUtils = require('react-dom/test-utils'); @@ -31,6 +41,17 @@ describe('ReactElement', () => { }; }); + afterEach(() => { + if (enableSymbolFallbackForWWW) { + global.Symbol = originalSymbol; + } + }); + + // @gate enableSymbolFallbackForWWW + it('uses the fallback value when in an environment without Symbol', () => { + expect((
).$$typeof).toBe(0xeac7); + }); + it('returns a complete element according to spec', () => { const element = React.createElement(ComponentClass); expect(element.type).toBe(ComponentClass); @@ -280,6 +301,42 @@ describe('ReactElement', () => { expect(element.type.someStaticMethod()).toBe('someReturnValue'); }); + // NOTE: We're explicitly not using JSX here. This is intended to test + // classic JS without JSX. + // @gate enableSymbolFallbackForWWW + it('identifies valid elements', () => { + class Component extends React.Component { + render() { + return React.createElement('div'); + } + } + + expect(React.isValidElement(React.createElement('div'))).toEqual(true); + expect(React.isValidElement(React.createElement(Component))).toEqual(true); + + expect(React.isValidElement(null)).toEqual(false); + expect(React.isValidElement(true)).toEqual(false); + expect(React.isValidElement({})).toEqual(false); + expect(React.isValidElement('string')).toEqual(false); + if (!__EXPERIMENTAL__) { + let factory; + expect(() => { + factory = React.createFactory('div'); + }).toWarnDev( + 'Warning: React.createFactory() is deprecated and will be removed in a ' + + 'future major release. Consider using JSX or use React.createElement() ' + + 'directly instead.', + {withoutStack: true}, + ); + expect(React.isValidElement(factory)).toEqual(false); + } + expect(React.isValidElement(Component)).toEqual(false); + expect(React.isValidElement({type: 'div', props: {}})).toEqual(false); + + const jsonElement = JSON.stringify(React.createElement('div')); + expect(React.isValidElement(JSON.parse(jsonElement))).toBe(true); + }); + // NOTE: We're explicitly not using JSX here. This is intended to test // classic JS without JSX. it('is indistinguishable from a plain object', () => { @@ -397,6 +454,7 @@ describe('ReactElement', () => { // NOTE: We're explicitly not using JSX here. This is intended to test // classic JS without JSX. + // @gate !enableSymbolFallbackForWWW it('identifies elements, but not JSON, if Symbols are supported', () => { class Component extends React.Component { render() { @@ -429,4 +487,58 @@ describe('ReactElement', () => { const jsonElement = JSON.stringify(React.createElement('div')); expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false); }); + + // NOTE: We're explicitly not using JSX here. This is intended to test + // classic JS without JSX. + it('identifies elements, but not JSON, if Symbols are supported (with polyfill)', () => { + // Rudimentary polyfill + // Once all jest engines support Symbols natively we can swap this to test + // WITH native Symbols by default. + const REACT_ELEMENT_TYPE = function() {}; // fake Symbol + const OTHER_SYMBOL = function() {}; // another fake Symbol + global.Symbol = function(name) { + return OTHER_SYMBOL; + }; + global.Symbol.for = function(key) { + if (key === 'react.element') { + return REACT_ELEMENT_TYPE; + } + return OTHER_SYMBOL; + }; + + jest.resetModules(); + + React = require('react'); + + class Component extends React.Component { + render() { + return React.createElement('div'); + } + } + + expect(React.isValidElement(React.createElement('div'))).toEqual(true); + expect(React.isValidElement(React.createElement(Component))).toEqual(true); + + expect(React.isValidElement(null)).toEqual(false); + expect(React.isValidElement(true)).toEqual(false); + expect(React.isValidElement({})).toEqual(false); + expect(React.isValidElement('string')).toEqual(false); + if (!__EXPERIMENTAL__) { + let factory; + expect(() => { + factory = React.createFactory('div'); + }).toWarnDev( + 'Warning: React.createFactory() is deprecated and will be removed in a ' + + 'future major release. Consider using JSX or use React.createElement() ' + + 'directly instead.', + {withoutStack: true}, + ); + expect(React.isValidElement(factory)).toEqual(false); + } + expect(React.isValidElement(Component)).toEqual(false); + expect(React.isValidElement({type: 'div', props: {}})).toEqual(false); + + const jsonElement = JSON.stringify(React.createElement('div')); + expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false); + }); }); diff --git a/packages/react/src/__tests__/ReactElementJSX-test.js b/packages/react/src/__tests__/ReactElementJSX-test.js index 58ab4f69f5b9a..16b53923ffb5d 100644 --- a/packages/react/src/__tests__/ReactElementJSX-test.js +++ b/packages/react/src/__tests__/ReactElementJSX-test.js @@ -9,6 +9,8 @@ 'use strict'; +import {enableSymbolFallbackForWWW} from 'shared/ReactFeatureFlags'; + let React; let ReactDOM; let ReactTestUtils; @@ -20,9 +22,18 @@ let JSXDEVRuntime; // A lot of these tests are pulled from ReactElement-test because // this api is meant to be backwards compatible. describe('ReactElement.jsx', () => { + let originalSymbol; + beforeEach(() => { jest.resetModules(); + if (enableSymbolFallbackForWWW) { + // Delete the native Symbol if we have one to ensure we test the + // unpolyfilled environment. + originalSymbol = global.Symbol; + global.Symbol = undefined; + } + React = require('react'); JSXRuntime = require('react/jsx-runtime'); JSXDEVRuntime = require('react/jsx-dev-runtime'); @@ -30,6 +41,12 @@ describe('ReactElement.jsx', () => { ReactTestUtils = require('react-dom/test-utils'); }); + afterEach(() => { + if (enableSymbolFallbackForWWW) { + global.Symbol = originalSymbol; + } + }); + it('allows static methods to be called using the type property', () => { class StaticMethodComponentClass extends React.Component { render() { @@ -42,6 +59,48 @@ describe('ReactElement.jsx', () => { expect(element.type.someStaticMethod()).toBe('someReturnValue'); }); + // @gate enableSymbolFallbackForWWW + it('identifies valid elements', () => { + class Component extends React.Component { + render() { + return JSXRuntime.jsx('div', {}); + } + } + + expect(React.isValidElement(JSXRuntime.jsx('div', {}))).toEqual(true); + expect(React.isValidElement(JSXRuntime.jsx(Component, {}))).toEqual(true); + expect( + React.isValidElement(JSXRuntime.jsx(JSXRuntime.Fragment, {})), + ).toEqual(true); + if (__DEV__) { + expect(React.isValidElement(JSXDEVRuntime.jsxDEV('div', {}))).toEqual( + true, + ); + } + + expect(React.isValidElement(null)).toEqual(false); + expect(React.isValidElement(true)).toEqual(false); + expect(React.isValidElement({})).toEqual(false); + expect(React.isValidElement('string')).toEqual(false); + if (!__EXPERIMENTAL__) { + let factory; + expect(() => { + factory = React.createFactory('div'); + }).toWarnDev( + 'Warning: React.createFactory() is deprecated and will be removed in a ' + + 'future major release. Consider using JSX or use React.createElement() ' + + 'directly instead.', + {withoutStack: true}, + ); + expect(React.isValidElement(factory)).toEqual(false); + } + expect(React.isValidElement(Component)).toEqual(false); + expect(React.isValidElement({type: 'div', props: {}})).toEqual(false); + + const jsonElement = JSON.stringify(JSXRuntime.jsx('div', {})); + expect(React.isValidElement(JSON.parse(jsonElement))).toBe(true); + }); + it('is indistinguishable from a plain object', () => { const element = JSXRuntime.jsx('div', {className: 'foo'}); const object = {}; @@ -235,6 +294,7 @@ describe('ReactElement.jsx', () => { ); }); + // @gate !enableSymbolFallbackForWWW it('identifies elements, but not JSON, if Symbols are supported', () => { class Component extends React.Component { render() { @@ -276,6 +336,59 @@ describe('ReactElement.jsx', () => { expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false); }); + it('identifies elements, but not JSON, if Symbols are polyfilled', () => { + // Rudimentary polyfill + // Once all jest engines support Symbols natively we can swap this to test + // WITH native Symbols by default. + const REACT_ELEMENT_TYPE = function() {}; // fake Symbol + const OTHER_SYMBOL = function() {}; // another fake Symbol + global.Symbol = function(name) { + return OTHER_SYMBOL; + }; + global.Symbol.for = function(key) { + if (key === 'react.element') { + return REACT_ELEMENT_TYPE; + } + return OTHER_SYMBOL; + }; + + jest.resetModules(); + + React = require('react'); + JSXRuntime = require('react/jsx-runtime'); + + class Component extends React.Component { + render() { + return JSXRuntime.jsx('div'); + } + } + + expect(React.isValidElement(JSXRuntime.jsx('div', {}))).toEqual(true); + expect(React.isValidElement(JSXRuntime.jsx(Component, {}))).toEqual(true); + + expect(React.isValidElement(null)).toEqual(false); + expect(React.isValidElement(true)).toEqual(false); + expect(React.isValidElement({})).toEqual(false); + expect(React.isValidElement('string')).toEqual(false); + if (!__EXPERIMENTAL__) { + let factory; + expect(() => { + factory = React.createFactory('div'); + }).toWarnDev( + 'Warning: React.createFactory() is deprecated and will be removed in a ' + + 'future major release. Consider using JSX or use React.createElement() ' + + 'directly instead.', + {withoutStack: true}, + ); + expect(React.isValidElement(factory)).toEqual(false); + } + expect(React.isValidElement(Component)).toEqual(false); + expect(React.isValidElement({type: 'div', props: {}})).toEqual(false); + + const jsonElement = JSON.stringify(JSXRuntime.jsx('div', {})); + expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false); + }); + it('should warn when unkeyed children are passed to jsx', () => { const container = document.createElement('div'); diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 33261df6d76d7..aafe4c74d0440 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -28,6 +28,9 @@ export const enablePersistentOffscreenHostContainer = false; // like migrating internal callers or performance testing. // ----------------------------------------------------------------------------- +// This is blocked on adding a symbol polyfill to www. +export const enableSymbolFallbackForWWW = false; + // This rolled out to 10% public in www, so we should be able to land, but some // internal tests need to be updated. The open source behavior is correct. export const skipUnmountedBoundaries = true; diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index 6ff9305fa7b00..523cc1436dfc2 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -11,33 +11,72 @@ // When adding new symbols to this file, // Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols' +import {enableSymbolFallbackForWWW} from './ReactFeatureFlags'; + +const usePolyfill = + enableSymbolFallbackForWWW && (typeof Symbol !== 'function' || !Symbol.for); + // The Symbol used to tag the ReactElement-like types. -export const REACT_ELEMENT_TYPE = Symbol.for('react.element'); -export const REACT_PORTAL_TYPE = Symbol.for('react.portal'); -export const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment'); -export const REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode'); -export const REACT_PROFILER_TYPE = Symbol.for('react.profiler'); -export const REACT_PROVIDER_TYPE = Symbol.for('react.provider'); -export const REACT_CONTEXT_TYPE = Symbol.for('react.context'); -export const REACT_SERVER_CONTEXT_TYPE = Symbol.for('react.server_context'); -export const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref'); -export const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense'); -export const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list'); -export const REACT_MEMO_TYPE = Symbol.for('react.memo'); -export const REACT_LAZY_TYPE = Symbol.for('react.lazy'); -export const REACT_SCOPE_TYPE = Symbol.for('react.scope'); -export const REACT_DEBUG_TRACING_MODE_TYPE = Symbol.for( - 'react.debug_trace_mode', -); -export const REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen'); -export const REACT_LEGACY_HIDDEN_TYPE = Symbol.for('react.legacy_hidden'); -export const REACT_CACHE_TYPE = Symbol.for('react.cache'); -export const REACT_TRACING_MARKER_TYPE = Symbol.for('react.tracing_marker'); -export const REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = Symbol.for( - 'react.default_value', -); +export const REACT_ELEMENT_TYPE = usePolyfill + ? 0xeac7 + : Symbol.for('react.element'); +export const REACT_PORTAL_TYPE = usePolyfill + ? 0xeaca + : Symbol.for('react.portal'); +export const REACT_FRAGMENT_TYPE = usePolyfill + ? 0xeacb + : Symbol.for('react.fragment'); +export const REACT_STRICT_MODE_TYPE = usePolyfill + ? 0xeacc + : Symbol.for('react.strict_mode'); +export const REACT_PROFILER_TYPE = usePolyfill + ? 0xead2 + : Symbol.for('react.profiler'); +export const REACT_PROVIDER_TYPE = usePolyfill + ? 0xeacd + : Symbol.for('react.provider'); +export const REACT_CONTEXT_TYPE = usePolyfill + ? 0xeace + : Symbol.for('react.context'); +export const REACT_SERVER_CONTEXT_TYPE = usePolyfill + ? 0xeae6 + : Symbol.for('react.server_context'); +export const REACT_FORWARD_REF_TYPE = usePolyfill + ? 0xead0 + : Symbol.for('react.forward_ref'); +export const REACT_SUSPENSE_TYPE = usePolyfill + ? 0xead1 + : Symbol.for('react.suspense'); +export const REACT_SUSPENSE_LIST_TYPE = usePolyfill + ? 0xead8 + : Symbol.for('react.suspense_list'); +export const REACT_MEMO_TYPE = usePolyfill ? 0xead3 : Symbol.for('react.memo'); +export const REACT_LAZY_TYPE = usePolyfill ? 0xead4 : Symbol.for('react.lazy'); +export const REACT_SCOPE_TYPE = usePolyfill + ? 0xead7 + : Symbol.for('react.scope'); +export const REACT_DEBUG_TRACING_MODE_TYPE = usePolyfill + ? 0xeae1 + : Symbol.for('react.debug_trace_mode'); +export const REACT_OFFSCREEN_TYPE = usePolyfill + ? 0xeae2 + : Symbol.for('react.offscreen'); +export const REACT_LEGACY_HIDDEN_TYPE = usePolyfill + ? 0xeae3 + : Symbol.for('react.legacy_hidden'); +export const REACT_CACHE_TYPE = usePolyfill + ? 0xeae4 + : Symbol.for('react.cache'); +export const REACT_TRACING_MARKER_TYPE = usePolyfill + ? 0xeae5 + : Symbol.for('react.tracing_marker'); +export const REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = usePolyfill + ? 0xeae7 + : Symbol.for('react.default_value'); +const MAYBE_ITERATOR_SYMBOL = usePolyfill + ? typeof Symbol === 'function' && Symbol.iterator + : Symbol.iterator; -const MAYBE_ITERATOR_SYMBOL = Symbol.iterator; const FAUX_ITERATOR_SYMBOL = '@@iterator'; export function getIteratorFn(maybeIterable: ?any): ?() => ?Iterator<*> { diff --git a/packages/shared/__tests__/ReactSymbols-test.internal.js b/packages/shared/__tests__/ReactSymbols-test.internal.js index 53618ba3fe4fa..df16a51c7c8ff 100644 --- a/packages/shared/__tests__/ReactSymbols-test.internal.js +++ b/packages/shared/__tests__/ReactSymbols-test.internal.js @@ -26,4 +26,20 @@ describe('ReactSymbols', () => { it('Symbol values should be unique', () => { expectToBeUnique(Object.entries(require('shared/ReactSymbols'))); }); + + // @gate enableSymbolFallbackForWWW + it('numeric values should be unique', () => { + const originalSymbolFor = global.Symbol.for; + global.Symbol.for = null; + try { + const entries = Object.entries(require('shared/ReactSymbols')).filter( + // REACT_ASYNC_MODE_TYPE and REACT_CONCURRENT_MODE_TYPE have the same numeric value + // for legacy backwards compatibility + ([key]) => key !== 'REACT_ASYNC_MODE_TYPE', + ); + expectToBeUnique(entries); + } finally { + global.Symbol.for = originalSymbolFor; + } + }); }); diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index facc9c7d78369..f2ce77374235e 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -83,6 +83,7 @@ export const enableServerContext = false; export const enableUseMutableSource = true; export const enableTransitionTracing = false; +export const enableSymbolFallbackForWWW = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 94bd838658962..43855df94f90d 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -74,6 +74,7 @@ export const enableServerContext = false; export const enableUseMutableSource = false; export const enableTransitionTracing = false; +export const enableSymbolFallbackForWWW = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index b0c81a7a8bb34..4283aa16e1201 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -74,6 +74,7 @@ export const enableServerContext = false; export const enableUseMutableSource = false; export const enableTransitionTracing = false; +export const enableSymbolFallbackForWWW = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index e05dc109843c7..938b4d136d73d 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -72,6 +72,7 @@ export const enableServerContext = false; export const enableUseMutableSource = false; export const enableTransitionTracing = false; +export const enableSymbolFallbackForWWW = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 042c2519043b1..7ad5cc56d9d92 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -76,6 +76,7 @@ export const enableServerContext = false; export const enableUseMutableSource = true; export const enableTransitionTracing = false; +export const enableSymbolFallbackForWWW = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index 6b640311000fb..0f65c001e5402 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -74,6 +74,7 @@ export const enableServerContext = false; export const enableUseMutableSource = false; export const enableTransitionTracing = false; +export const enableSymbolFallbackForWWW = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index 5e41ca9992da4..93d9860413db8 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -75,6 +75,7 @@ export const enableServerContext = false; export const enableUseMutableSource = true; export const enableTransitionTracing = false; +export const enableSymbolFallbackForWWW = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index 1f7de0b6b5a10..cfc32d6bacff1 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -29,7 +29,7 @@ export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = __ export const enableClientRenderFallbackOnHydrationMismatch = __VARIANT__; export const enableClientRenderFallbackOnTextMismatch = __VARIANT__; export const enableTransitionTracing = __VARIANT__; - +export const enableSymbolFallbackForWWW = __VARIANT__; // Enable this flag to help with concurrent mode debugging. // It logs information to the console about React scheduling, rendering, and commit phases. // diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index d5edae5563a5b..a108f7a144b9e 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -115,6 +115,7 @@ export const enableCustomElementPropertySupport = __EXPERIMENTAL__; export const enableTransitionTracing = false; +export const enableSymbolFallbackForWWW = true; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars type Check<_X, Y: _X, X: Y = _X> = null; diff --git a/packages/shared/isValidElementType.js b/packages/shared/isValidElementType.js index 87424982afc11..598a53666f60c 100644 --- a/packages/shared/isValidElementType.js +++ b/packages/shared/isValidElementType.js @@ -31,9 +31,19 @@ import { enableTransitionTracing, enableDebugTracing, enableLegacyHidden, + enableSymbolFallbackForWWW, } from './ReactFeatureFlags'; -const REACT_MODULE_REFERENCE: Symbol = Symbol.for('react.module.reference'); +let REACT_MODULE_REFERENCE; +if (enableSymbolFallbackForWWW) { + if (typeof Symbol === 'function') { + REACT_MODULE_REFERENCE = Symbol.for('react.module.reference'); + } else { + REACT_MODULE_REFERENCE = 0; + } +} else { + REACT_MODULE_REFERENCE = Symbol.for('react.module.reference'); +} export default function isValidElementType(type: mixed) { if (typeof type === 'string' || typeof type === 'function') {