Skip to content

Commit

Permalink
Revert "Remove blocking mode and blocking root (#20888)"
Browse files Browse the repository at this point in the history
This reverts commit 553440b.
  • Loading branch information
acdlite authored Mar 2, 2021
1 parent de0ee76 commit bbb3e97
Show file tree
Hide file tree
Showing 36 changed files with 531 additions and 81 deletions.
2 changes: 2 additions & 0 deletions packages/react-dom/index.classic.fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export {
unmountComponentAtNode,
createRoot,
createRoot as unstable_createRoot,
createBlockingRoot,
createBlockingRoot as unstable_createBlockingRoot,
unstable_flushControlled,
unstable_scheduleHydration,
unstable_runWithPriority,
Expand Down
1 change: 1 addition & 0 deletions packages/react-dom/index.experimental.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export {
unmountComponentAtNode,
// exposeConcurrentModeAPIs
createRoot as unstable_createRoot,
createBlockingRoot as unstable_createBlockingRoot,
unstable_flushControlled,
unstable_scheduleHydration,
// DO NOT USE: Temporarily exposing this to migrate off of Scheduler.runWithPriority.
Expand Down
2 changes: 2 additions & 0 deletions packages/react-dom/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export {
unmountComponentAtNode,
createRoot,
createRoot as unstable_createRoot,
createBlockingRoot,
createBlockingRoot as unstable_createBlockingRoot,
unstable_flushControlled,
unstable_scheduleHydration,
unstable_runWithPriority,
Expand Down
2 changes: 2 additions & 0 deletions packages/react-dom/index.modern.fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export {
version,
createRoot,
createRoot as unstable_createRoot,
createBlockingRoot,
createBlockingRoot as unstable_createBlockingRoot,
unstable_flushControlled,
unstable_scheduleHydration,
unstable_runWithPriority,
Expand Down
27 changes: 27 additions & 0 deletions packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,33 @@ describe('ReactDOMFiberAsync', () => {
expect(containerC.textContent).toEqual('Finished');
});

describe('createBlockingRoot', () => {
// @gate experimental
it('updates flush without yielding in the next event', () => {
const root = ReactDOM.unstable_createBlockingRoot(container);

function Text(props) {
Scheduler.unstable_yieldValue(props.text);
return props.text;
}

root.render(
<>
<Text text="A" />
<Text text="B" />
<Text text="C" />
</>,
);

// Nothing should have rendered yet
expect(container.textContent).toEqual('');

// Everything should render immediately in the next event
expect(Scheduler).toFlushExpired(['A', 'B', 'C']);
expect(container.textContent).toEqual('ABC');
});
});

// @gate experimental
it('unmounted roots should never clear newer root content from a container', () => {
const ref = React.createRef();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ describe('ReactDOMServerPartialHydration', () => {
}).toErrorDev(
'Warning: Cannot hydrate Suspense in legacy mode. Switch from ' +
'ReactDOM.hydrate(element, container) to ' +
'ReactDOM.createRoot(container, { hydrate: true })' +
'ReactDOM.createBlockingRoot(container, { hydrate: true })' +
'.render(element) or remove the Suspense components from the server ' +
'rendered components.' +
'\n in Suspense (at **)' +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ describe('ReactDOMServerSuspense', () => {
expect(divB.textContent).toBe('B');

act(() => {
const root = ReactDOM.createRoot(parent, {hydrate: true});
const root = ReactDOM.createBlockingRoot(parent, {hydrate: true});
root.render(example);
});

Expand Down
58 changes: 52 additions & 6 deletions packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,33 @@ describe('ReactTestUtils.act()', () => {

runActTests('legacy mode', renderLegacy, unmountLegacy, rerenderLegacy);

// and then in blocking mode
if (__EXPERIMENTAL__) {
let blockingRoot = null;
const renderBatched = (el, dom) => {
blockingRoot = ReactDOM.unstable_createBlockingRoot(dom);
blockingRoot.render(el);
};

const unmountBatched = dom => {
if (blockingRoot !== null) {
blockingRoot.unmount();
blockingRoot = null;
}
};

const rerenderBatched = el => {
blockingRoot.render(el);
};

runActTests(
'blocking mode',
renderBatched,
unmountBatched,
rerenderBatched,
);
}

describe('unacted effects', () => {
function App() {
React.useEffect(() => {}, []);
Expand All @@ -97,6 +124,19 @@ describe('ReactTestUtils.act()', () => {
]);
});

// @gate experimental
it('warns in blocking mode', () => {
expect(() => {
const root = ReactDOM.unstable_createBlockingRoot(
document.createElement('div'),
);
root.render(<App />);
Scheduler.unstable_flushAll();
}).toErrorDev([
'An update to App ran an effect, but was not wrapped in act(...)',
]);
});

// @gate experimental
it('warns in concurrent mode', () => {
expect(() => {
Expand Down Expand Up @@ -691,10 +731,14 @@ function runActTests(label, render, unmount, rerender) {

it('triggers fallbacks if available', async () => {
if (label !== 'legacy mode') {
// FIXME: Support for Concurrent Root intentionally removed
// from the public version of `act`. It will be added back in
// a future major version, Concurrent Root officially released.
// Consider skipping all non-Legacy tests in this suite until then.
// FIXME: Support for Blocking* and Concurrent Mode were
// intentionally removed from the public version of `act`. It will
// be added back in a future major version, before Blocking and and
// Concurrent Mode are officially released. Consider disabling all
// non-Legacy tests in this suite until then.
//
// *Blocking Mode actually does happen to work, though
// not "officially" since it's an unreleased feature.
return;
}

Expand Down Expand Up @@ -750,8 +794,10 @@ function runActTests(label, render, unmount, rerender) {
// In Concurrent Mode, refresh transitions delay indefinitely.
expect(document.querySelector('[data-test-id=spinner]')).toBeNull();
} else {
// In Legacy Mode, all fallbacks are forced to display,
// even during a refresh transition.
// In Legacy Mode and Blocking Mode, all fallbacks are forced to
// display, even during a refresh transition.
// TODO: Consider delaying indefinitely in Blocking Mode, to match
// Concurrent Mode semantics.
expect(
document.querySelector('[data-test-id=spinner]'),
).not.toBeNull();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,22 @@ it('should warn when rendering in concurrent mode', () => {
ReactDOM.unstable_createRoot(document.createElement('div')).render(<App />);
}).toErrorDev([]);
});

// @gate experimental
it('should warn when rendering in blocking mode', () => {
expect(() => {
ReactDOM.unstable_createBlockingRoot(document.createElement('div')).render(
<App />,
);
}).toErrorDev(
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked ' +
'to guarantee consistent behaviour across tests and browsers.',
{withoutStack: true},
);
// does not warn twice
expect(() => {
ReactDOM.unstable_createBlockingRoot(document.createElement('div')).render(
<App />,
);
}).toErrorDev([]);
});
3 changes: 2 additions & 1 deletion packages/react-dom/src/client/ReactDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
unstable_renderSubtreeIntoContainer,
unmountComponentAtNode,
} from './ReactDOMLegacy';
import {createRoot, isValidContainer} from './ReactDOMRoot';
import {createRoot, createBlockingRoot, isValidContainer} from './ReactDOMRoot';
import {createEventHandle} from './ReactDOMEventHandle';

import {
Expand Down Expand Up @@ -201,6 +201,7 @@ export {
unmountComponentAtNode,
// exposeConcurrentModeAPIs
createRoot,
createBlockingRoot,
flushControlled as unstable_flushControlled,
scheduleHydration as unstable_scheduleHydration,
// Disabled behind disableUnstableRenderSubtreeIntoContainer
Expand Down
32 changes: 26 additions & 6 deletions packages/react-dom/src/client/ReactDOMRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,25 @@ import {
registerMutableSourceForHydration,
} from 'react-reconciler/src/ReactFiberReconciler';
import invariant from 'shared/invariant';
import {ConcurrentRoot, LegacyRoot} from 'react-reconciler/src/ReactRootTags';
import {
BlockingRoot,
ConcurrentRoot,
LegacyRoot,
} from 'react-reconciler/src/ReactRootTags';

function ReactDOMRoot(container: Container, options: void | RootOptions) {
this._internalRoot = createRootImpl(container, ConcurrentRoot, options);
}

function ReactDOMLegacyRoot(container: Container, options: void | RootOptions) {
this._internalRoot = createRootImpl(container, LegacyRoot, options);
function ReactDOMBlockingRoot(
container: Container,
tag: RootTag,
options: void | RootOptions,
) {
this._internalRoot = createRootImpl(container, tag, options);
}

ReactDOMRoot.prototype.render = ReactDOMLegacyRoot.prototype.render = function(
ReactDOMRoot.prototype.render = ReactDOMBlockingRoot.prototype.render = function(
children: ReactNodeList,
): void {
const root = this._internalRoot;
Expand Down Expand Up @@ -91,7 +99,7 @@ ReactDOMRoot.prototype.render = ReactDOMLegacyRoot.prototype.render = function(
updateContainer(children, root, null, null);
};

ReactDOMRoot.prototype.unmount = ReactDOMLegacyRoot.prototype.unmount = function(): void {
ReactDOMRoot.prototype.unmount = ReactDOMBlockingRoot.prototype.unmount = function(): void {
if (__DEV__) {
if (typeof arguments[0] === 'function') {
console.error(
Expand Down Expand Up @@ -161,11 +169,23 @@ export function createRoot(
return new ReactDOMRoot(container, options);
}

export function createBlockingRoot(
container: Container,
options?: RootOptions,
): RootType {
invariant(
isValidContainer(container),
'createRoot(...): Target container is not a DOM element.',
);
warnIfReactDOMContainerInDEV(container);
return new ReactDOMBlockingRoot(container, BlockingRoot, options);
}

export function createLegacyRoot(
container: Container,
options?: RootOptions,
): RootType {
return new ReactDOMLegacyRoot(container, options);
return new ReactDOMBlockingRoot(container, LegacyRoot, options);
}

export function isValidContainer(node: mixed): boolean {
Expand Down
1 change: 1 addition & 0 deletions packages/react-noop-renderer/src/ReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const {
getPendingChildren,
getOrCreateRootContainer,
createRoot,
createBlockingRoot,
createLegacyRoot,
getChildrenAsJSX,
getPendingChildrenAsJSX,
Expand Down
1 change: 1 addition & 0 deletions packages/react-noop-renderer/src/ReactNoopPersistent.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const {
getPendingChildren,
getOrCreateRootContainer,
createRoot,
createBlockingRoot,
createLegacyRoot,
getChildrenAsJSX,
getPendingChildrenAsJSX,
Expand Down
33 changes: 32 additions & 1 deletion packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ import type {RootTag} from 'react-reconciler/src/ReactRootTags';

import * as Scheduler from 'scheduler/unstable_mock';
import {REACT_FRAGMENT_TYPE, REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
import {ConcurrentRoot, LegacyRoot} from 'react-reconciler/src/ReactRootTags';
import {
ConcurrentRoot,
BlockingRoot,
LegacyRoot,
} from 'react-reconciler/src/ReactRootTags';

import {
enableNativeEventPriorityInference,
Expand Down Expand Up @@ -752,6 +756,33 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
};
},

createBlockingRoot() {
const container = {
rootID: '' + idCounter++,
pendingChildren: [],
children: [],
};
const fiberRoot = NoopRenderer.createContainer(
container,
BlockingRoot,
false,
null,
null,
);
return {
_Scheduler: Scheduler,
render(children: ReactNodeList) {
NoopRenderer.updateContainer(children, fiberRoot, null, null);
},
getChildren() {
return getChildren(container);
},
getChildrenAsJSX() {
return getChildrenAsJSX(container);
},
};
},

createLegacyRoot() {
const container = {
rootID: '' + idCounter++,
Expand Down
23 changes: 21 additions & 2 deletions packages/react-reconciler/src/ReactFiber.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
enableScopeAPI,
} from 'shared/ReactFeatureFlags';
import {NoFlags, Placement, StaticMask} from './ReactFiberFlags';
import {ConcurrentRoot} from './ReactRootTags';
import {ConcurrentRoot, BlockingRoot} from './ReactRootTags';
import {
IndeterminateComponent,
ClassComponent,
Expand Down Expand Up @@ -68,6 +68,7 @@ import {
ProfileMode,
StrictLegacyMode,
StrictEffectsMode,
BlockingMode,
} from './ReactTypeOfMode';
import {
REACT_FORWARD_REF_TYPE,
Expand Down Expand Up @@ -426,7 +427,25 @@ export function createHostRootFiber(
): Fiber {
let mode;
if (tag === ConcurrentRoot) {
mode = ConcurrentMode;
mode = ConcurrentMode | BlockingMode;
if (strictModeLevelOverride !== null) {
if (strictModeLevelOverride >= 1) {
mode |= StrictLegacyMode;
}
if (enableStrictEffects) {
if (strictModeLevelOverride >= 2) {
mode |= StrictEffectsMode;
}
}
} else {
if (enableStrictEffects && createRootStrictEffectsByDefault) {
mode |= StrictLegacyMode | StrictEffectsMode;
} else {
mode |= StrictLegacyMode;
}
}
} else if (tag === BlockingRoot) {
mode = BlockingMode;
if (strictModeLevelOverride !== null) {
if (strictModeLevelOverride >= 1) {
mode |= StrictLegacyMode;
Expand Down
Loading

0 comments on commit bbb3e97

Please sign in to comment.