From 34348a45b4b1de40d285ba7168e4d6770e620911 Mon Sep 17 00:00:00 2001 From: Alex Taylor Date: Wed, 5 Sep 2018 15:04:59 -0700 Subject: [PATCH] Add enableSuspenseServerRenderer feature flag (#13573) --- ...eactDOMServerPlaceholders-test.internal.js | 59 +++++++++++++++++++ .../src/server/ReactPartialRenderer.js | 28 ++++++++- packages/shared/ReactFeatureFlags.js | 3 + .../ReactFeatureFlags.native-fabric-fb.js | 1 + .../ReactFeatureFlags.native-fabric-oss.js | 1 + .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.persistent.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.www.js | 1 + 11 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 packages/react-dom/src/__tests__/ReactDOMServerPlaceholders-test.internal.js diff --git a/packages/react-dom/src/__tests__/ReactDOMServerPlaceholders-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerPlaceholders-test.internal.js new file mode 100644 index 0000000000..5f508e4b3f --- /dev/null +++ b/packages/react-dom/src/__tests__/ReactDOMServerPlaceholders-test.internal.js @@ -0,0 +1,59 @@ +/** + * 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. + * + * @emails react-core + */ + +'use strict'; + +const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils'); + +let React; +let ReactDOM; +let ReactDOMServer; +let ReactFeatureFlags; + +function initModules() { + // Reset warning cache. + jest.resetModuleRegistry(); + + ReactFeatureFlags = require('shared/ReactFeatureFlags'); + ReactFeatureFlags.enableSuspense = true; + ReactFeatureFlags.enableSuspenseServerRenderer = true; + + React = require('react'); + ReactDOM = require('react-dom'); + ReactDOMServer = require('react-dom/server'); + + // Make them available to the helpers. + return { + ReactDOM, + ReactDOMServer, + }; +} + +const {resetModules, serverRender} = ReactDOMServerIntegrationUtils( + initModules, +); + +describe('ReactDOMServerPlaceholders', () => { + beforeEach(() => { + resetModules(); + }); + + it('should always render the fallback when a placeholder is encountered', async () => { + const Suspended = props => { + throw new Promise(() => {}); + }; + const e = await serverRender( + }> + + , + ); + + expect(e.tagName).toBe('DIV'); + }); +}); diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 230806536b..a8596b7c3d 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -23,12 +23,17 @@ import warningWithoutStack from 'shared/warningWithoutStack'; import checkPropTypes from 'prop-types/checkPropTypes'; import describeComponentFrame from 'shared/describeComponentFrame'; import ReactSharedInternals from 'shared/ReactSharedInternals'; -import {warnAboutDeprecatedLifecycles} from 'shared/ReactFeatureFlags'; +import { + warnAboutDeprecatedLifecycles, + enableSuspenseServerRenderer, +} from 'shared/ReactFeatureFlags'; + import { REACT_FORWARD_REF_TYPE, REACT_FRAGMENT_TYPE, REACT_STRICT_MODE_TYPE, REACT_ASYNC_MODE_TYPE, + REACT_PLACEHOLDER_TYPE, REACT_PORTAL_TYPE, REACT_PROFILER_TYPE, REACT_PROVIDER_TYPE, @@ -911,6 +916,27 @@ class ReactDOMServerRenderer { this.stack.push(frame); return ''; } + case REACT_PLACEHOLDER_TYPE: { + if (enableSuspenseServerRenderer) { + const nextChildren = toArray( + // Always use the fallback when synchronously rendering to string. + ((nextChild: any): ReactElement).props.fallback, + ); + const frame: Frame = { + type: null, + domNamespace: parentNamespace, + children: nextChildren, + childIndex: 0, + context: context, + footer: '', + }; + if (__DEV__) { + ((frame: any): FrameDev).debugElementStack = []; + } + this.stack.push(frame); + return ''; + } + } // eslint-disable-next-line-no-fallthrough default: break; diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index c0e53946ea..e5b009653d 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -42,6 +42,9 @@ export const enableProfilerTimer = __PROFILE__; // Track which interactions trigger each commit. export const enableSchedulerTracking = __PROFILE__; +// Only used in www builds. +export const enableSuspenseServerRenderer = false; + // Only used in www builds. export function addUserTimingListener() { invariant(false, 'Not implemented.'); diff --git a/packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js index 634a9236aa..5b4a2eb37c 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js @@ -22,6 +22,7 @@ export const warnAboutLegacyContextAPI = __DEV__; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; export const enableProfilerTimer = __PROFILE__; export const enableSchedulerTracking = __PROFILE__; +export const enableSuspenseServerRenderer = false; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js b/packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js index 210e40c449..3890fa12e6 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js @@ -22,6 +22,7 @@ export const warnAboutLegacyContextAPI = false; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; export const enableProfilerTimer = __PROFILE__; export const enableSchedulerTracking = __PROFILE__; +export const enableSuspenseServerRenderer = false; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index f86dd8c5cf..07a943c9b9 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -27,6 +27,7 @@ export const enableUserTimingAPI = __DEV__; export const warnAboutLegacyContextAPI = __DEV__; export const enableProfilerTimer = __PROFILE__; export const enableSchedulerTracking = __PROFILE__; +export const enableSuspenseServerRenderer = false; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 8097e29d95..08d6134447 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -22,6 +22,7 @@ export const warnAboutDeprecatedLifecycles = false; export const warnAboutLegacyContextAPI = false; export const enableProfilerTimer = __PROFILE__; export const enableSchedulerTracking = __PROFILE__; +export const enableSuspenseServerRenderer = false; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.persistent.js b/packages/shared/forks/ReactFeatureFlags.persistent.js index 0d78dacdf2..eb37c87c25 100644 --- a/packages/shared/forks/ReactFeatureFlags.persistent.js +++ b/packages/shared/forks/ReactFeatureFlags.persistent.js @@ -22,6 +22,7 @@ export const warnAboutLegacyContextAPI = false; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; export const enableProfilerTimer = __PROFILE__; export const enableSchedulerTracking = __PROFILE__; +export const enableSuspenseServerRenderer = false; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 280359f999..63a06b31b3 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -22,6 +22,7 @@ export const warnAboutLegacyContextAPI = false; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = false; export const enableSchedulerTracking = false; +export const enableSuspenseServerRenderer = false; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 4fa93944f3..daeb9a80e9 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -22,6 +22,7 @@ export const warnAboutLegacyContextAPI = false; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = false; export const enableSchedulerTracking = false; +export const enableSuspenseServerRenderer = false; // Only used in www builds. export function addUserTimingListener() { diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 5baa9cdff0..258a0dd3b3 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -16,6 +16,7 @@ export const { debugRenderPhaseSideEffects, debugRenderPhaseSideEffectsForStrictMode, enableGetDerivedStateFromCatch, + enableSuspenseServerRenderer, replayFailedUnitOfWorkWithInvokeGuardedCallback, warnAboutDeprecatedLifecycles, } = require('ReactFeatureFlags');