Skip to content

Commit cb15184

Browse files
authored
[react-dom] move all client code to react-dom/client (facebook#28271)
This PR reorganizes the `react-dom` entrypoint to only pull in code that is environment agnostic. Previously if you required anything from this entrypoint in any environment the entire client reconciler was loaded. In a prior release we added a server rendering stub which you could alias in server environments to omit this unecessary code. After landing this change this entrypoint should not load any environment specific code. While a few APIs are truly client (browser) only such as createRoot and hydrateRoot many of the APIs you import from this package are only useful in the browser but could concievably be imported in shared code (components running in Fizz or shared components as part of an RSC app). To avoid making these require opting into the client bundle we are keeping them in the `react-dom` entrypoint and changing their implementation so that in environments where they are not particularly useful they do something benign and expected. #### Removed APIs The following APIs are being removed in the next major. Largely they have all been deprecated already and are part of legacy rendering modes where concurrent features of React are not available * `render` * `hydrate` * `findDOMNode` * `unmountComponentAtNode` * `unstable_createEventHandle` * `unstable_renderSubtreeIntoContainer` * `unstable_runWithPrioirty` #### moved Client APIs These APIs were available on both `react-dom` (with a warning) and `react-dom/client`. After this change they are only available on `react-dom/client` * `createRoot` * `hydrateRoot` #### retained APIs These APIs still exist on the `react-dom` entrypoint but have normalized behavior depending on which renderers are currently in scope * `flushSync`: will execute the function (if provided) inside the flushSync implemention of FlightServer, Fizz, and Fiber DOM renderers. * `unstable_batchedUpdates`: This is a noop in concurrent mode because it is now the only supported behavior because there is no legacy rendering mode * `createPortal`: This just produces an object. It can be called from anywhere but since you will probably not have a handle on a DOM node to pass to it it will likely warn in environments other than the browser * preloading APIS such as `preload`: These methods will execute the preload across all renderers currently in scope. Since we resolve the Request object on the server using AsyncLocalStorage or the current function stack in practice only one renderer should act upon the preload. In addition to these changes the server rendering stub now just rexports everything from `react-dom`. In a future minor we will add a warning when using the stub and in the next major we will remove the stub altogether
1 parent b039be6 commit cb15184

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1715
-2052
lines changed

packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js

+1,204-1,164
Large diffs are not rendered by default.

packages/react-devtools-shared/src/__tests__/preprocessData-test.js

+35-35
Original file line numberDiff line numberDiff line change
@@ -841,83 +841,83 @@ describe('Timeline profiler', () => {
841841
{
842842
"batchUID": 0,
843843
"depth": 0,
844-
"duration": 0.012,
844+
"duration": 0.014,
845845
"lanes": "0b0000000000000000000000000000101",
846-
"timestamp": 0.006,
846+
"timestamp": 0.008,
847847
"type": "render-idle",
848848
},
849849
{
850850
"batchUID": 0,
851851
"depth": 0,
852852
"duration": 0.003,
853853
"lanes": "0b0000000000000000000000000000101",
854-
"timestamp": 0.006,
854+
"timestamp": 0.008,
855855
"type": "render",
856856
},
857857
{
858858
"batchUID": 0,
859859
"depth": 0,
860-
"duration": 0.008,
860+
"duration": 0.010,
861861
"lanes": "0b0000000000000000000000000000101",
862-
"timestamp": 0.01,
862+
"timestamp": 0.012,
863863
"type": "commit",
864864
},
865865
{
866866
"batchUID": 0,
867867
"depth": 1,
868868
"duration": 0.001,
869869
"lanes": "0b0000000000000000000000000000101",
870-
"timestamp": 0.016,
870+
"timestamp": 0.02,
871871
"type": "layout-effects",
872872
},
873873
{
874874
"batchUID": 0,
875875
"depth": 0,
876876
"duration": 0.004,
877877
"lanes": "0b0000000000000000000000000000101",
878-
"timestamp": 0.019,
878+
"timestamp": 0.023,
879879
"type": "passive-effects",
880880
},
881881
],
882882
1 => [
883883
{
884884
"batchUID": 1,
885885
"depth": 0,
886-
"duration": 0.012,
886+
"duration": 0.014,
887887
"lanes": "0b0000000000000000000000000000101",
888-
"timestamp": 0.024,
888+
"timestamp": 0.028,
889889
"type": "render-idle",
890890
},
891891
{
892892
"batchUID": 1,
893893
"depth": 0,
894894
"duration": 0.003,
895895
"lanes": "0b0000000000000000000000000000101",
896-
"timestamp": 0.024,
896+
"timestamp": 0.028,
897897
"type": "render",
898898
},
899899
{
900900
"batchUID": 1,
901901
"depth": 0,
902-
"duration": 0.008,
902+
"duration": 0.010,
903903
"lanes": "0b0000000000000000000000000000101",
904-
"timestamp": 0.028,
904+
"timestamp": 0.032,
905905
"type": "commit",
906906
},
907907
{
908908
"batchUID": 1,
909909
"depth": 1,
910910
"duration": 0.001,
911911
"lanes": "0b0000000000000000000000000000101",
912-
"timestamp": 0.034,
912+
"timestamp": 0.04,
913913
"type": "layout-effects",
914914
},
915915
{
916916
"batchUID": 1,
917917
"depth": 0,
918918
"duration": 0.003,
919919
"lanes": "0b0000000000000000000000000000101",
920-
"timestamp": 0.037,
920+
"timestamp": 0.043,
921921
"type": "passive-effects",
922922
},
923923
],
@@ -926,33 +926,33 @@ describe('Timeline profiler', () => {
926926
{
927927
"componentName": "App",
928928
"duration": 0.001,
929-
"timestamp": 0.007,
929+
"timestamp": 0.009,
930930
"type": "render",
931931
"warning": null,
932932
},
933933
{
934934
"componentName": "App",
935935
"duration": 0.002,
936-
"timestamp": 0.02,
936+
"timestamp": 0.024,
937937
"type": "passive-effect-mount",
938938
"warning": null,
939939
},
940940
{
941941
"componentName": "App",
942942
"duration": 0.001,
943-
"timestamp": 0.025,
943+
"timestamp": 0.029,
944944
"type": "render",
945945
"warning": null,
946946
},
947947
{
948948
"componentName": "App",
949949
"duration": 0.001,
950-
"timestamp": 0.038,
950+
"timestamp": 0.044,
951951
"type": "passive-effect-mount",
952952
"warning": null,
953953
},
954954
],
955-
"duration": 0.04,
955+
"duration": 0.046,
956956
"flamechart": [],
957957
"internalModuleSourceToRanges": Map {
958958
undefined => [
@@ -1015,81 +1015,81 @@ describe('Timeline profiler', () => {
10151015
{
10161016
"batchUID": 0,
10171017
"depth": 0,
1018-
"duration": 0.012,
1018+
"duration": 0.014,
10191019
"lanes": "0b0000000000000000000000000000101",
1020-
"timestamp": 0.006,
1020+
"timestamp": 0.008,
10211021
"type": "render-idle",
10221022
},
10231023
{
10241024
"batchUID": 0,
10251025
"depth": 0,
10261026
"duration": 0.003,
10271027
"lanes": "0b0000000000000000000000000000101",
1028-
"timestamp": 0.006,
1028+
"timestamp": 0.008,
10291029
"type": "render",
10301030
},
10311031
{
10321032
"batchUID": 0,
10331033
"depth": 0,
1034-
"duration": 0.008,
1034+
"duration": 0.010,
10351035
"lanes": "0b0000000000000000000000000000101",
1036-
"timestamp": 0.01,
1036+
"timestamp": 0.012,
10371037
"type": "commit",
10381038
},
10391039
{
10401040
"batchUID": 0,
10411041
"depth": 1,
10421042
"duration": 0.001,
10431043
"lanes": "0b0000000000000000000000000000101",
1044-
"timestamp": 0.016,
1044+
"timestamp": 0.02,
10451045
"type": "layout-effects",
10461046
},
10471047
{
10481048
"batchUID": 0,
10491049
"depth": 0,
10501050
"duration": 0.004,
10511051
"lanes": "0b0000000000000000000000000000101",
1052-
"timestamp": 0.019,
1052+
"timestamp": 0.023,
10531053
"type": "passive-effects",
10541054
},
10551055
{
10561056
"batchUID": 1,
10571057
"depth": 0,
1058-
"duration": 0.012,
1058+
"duration": 0.014,
10591059
"lanes": "0b0000000000000000000000000000101",
1060-
"timestamp": 0.024,
1060+
"timestamp": 0.028,
10611061
"type": "render-idle",
10621062
},
10631063
{
10641064
"batchUID": 1,
10651065
"depth": 0,
10661066
"duration": 0.003,
10671067
"lanes": "0b0000000000000000000000000000101",
1068-
"timestamp": 0.024,
1068+
"timestamp": 0.028,
10691069
"type": "render",
10701070
},
10711071
{
10721072
"batchUID": 1,
10731073
"depth": 0,
1074-
"duration": 0.008,
1074+
"duration": 0.010,
10751075
"lanes": "0b0000000000000000000000000000101",
1076-
"timestamp": 0.028,
1076+
"timestamp": 0.032,
10771077
"type": "commit",
10781078
},
10791079
{
10801080
"batchUID": 1,
10811081
"depth": 1,
10821082
"duration": 0.001,
10831083
"lanes": "0b0000000000000000000000000000101",
1084-
"timestamp": 0.034,
1084+
"timestamp": 0.04,
10851085
"type": "layout-effects",
10861086
},
10871087
{
10881088
"batchUID": 1,
10891089
"depth": 0,
10901090
"duration": 0.003,
10911091
"lanes": "0b0000000000000000000000000000101",
1092-
"timestamp": 0.037,
1092+
"timestamp": 0.043,
10931093
"type": "passive-effects",
10941094
},
10951095
],
@@ -1126,14 +1126,14 @@ describe('Timeline profiler', () => {
11261126
"schedulingEvents": [
11271127
{
11281128
"lanes": "0b0000000000000000000000000000101",
1129-
"timestamp": 0.005,
1129+
"timestamp": 0.007,
11301130
"type": "schedule-render",
11311131
"warning": null,
11321132
},
11331133
{
11341134
"componentName": "App",
11351135
"lanes": "0b0000000000000000000000000000101",
1136-
"timestamp": 0.021,
1136+
"timestamp": 0.025,
11371137
"type": "schedule-state-update",
11381138
"warning": null,
11391139
},

packages/react-devtools-shell/src/e2e-regression/app.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@
33
// This test harness mounts each test app as a separate root to test multi-root applications.
44

55
import * as React from 'react';
6-
import * as ReactDOM from 'react-dom';
7-
import {createRoot} from 'react-dom/client';
6+
import * as ReactDOMClient from 'react-dom/client';
87
import ListApp from '../e2e-apps/ListApp';
98

109
function mountApp(App: () => React$Node) {
1110
const container = document.createElement('div');
1211

1312
((document.body: any): HTMLBodyElement).appendChild(container);
1413

15-
const root = createRoot(container);
14+
const root = ReactDOMClient.createRoot(container);
1615
root.render(<App />);
1716
}
1817
function mountTestApp() {
@@ -27,5 +26,5 @@ window.parent.REACT_DOM_APP = {
2726
createTestNameSelector: name => `[data-testname="${name}"]`,
2827
findAllNodes: (container, nodes) =>
2928
container.querySelectorAll(nodes.join(' ')),
30-
...ReactDOM,
29+
...ReactDOMClient,
3130
};

packages/react-devtools-shell/src/e2e-regression/devtools.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22
import * as ReactDOM from 'react-dom';
3-
import {createRoot} from 'react-dom/client';
3+
import * as ReactDOMClient from 'react-dom/client';
44
import {
55
activate as activateBackend,
66
initialize as initializeBackend,
@@ -32,7 +32,7 @@ function init(appIframe, devtoolsContainer, appSource) {
3232
const DevTools = createDevTools(contentWindow);
3333

3434
inject(contentDocument, appSource, () => {
35-
createRoot(devtoolsContainer).render(
35+
ReactDOMClient.createRoot(devtoolsContainer).render(
3636
<DevTools
3737
hookNamesModuleLoaderFunction={hookNamesModuleLoaderFunction}
3838
showTabBar={true}
@@ -55,4 +55,5 @@ init(
5555
);
5656

5757
// ReactDOM Test Selector APIs used by Playwright e2e tests
58-
window.parent.REACT_DOM_DEVTOOLS = ReactDOM;
58+
window.parent.REACT_DOM_DEVTOOLS =
59+
'createTestNameSelector' in ReactDOMClient ? ReactDOMClient : ReactDOM;

packages/react-devtools-shell/src/e2e/app.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
// This test harness mounts each test app as a separate root to test multi-root applications.
44

55
import * as React from 'react';
6-
import * as ReactDOM from 'react-dom';
7-
import {createRoot} from 'react-dom/client';
6+
import * as ReactDOMClient from 'react-dom/client';
87

98
const container = document.createElement('div');
109

@@ -14,8 +13,8 @@ const container = document.createElement('div');
1413
// so that it can load things other than just ToDoList.
1514
const App = require('../e2e-apps/ListApp').default;
1615

17-
const root = createRoot(container);
16+
const root = ReactDOMClient.createRoot(container);
1817
root.render(<App />);
1918

2019
// ReactDOM Test Selector APIs used by Playwright e2e tests
21-
window.parent.REACT_DOM_APP = ReactDOM;
20+
window.parent.REACT_DOM_APP = ReactDOMClient;

packages/react-devtools-shell/src/e2e/devtools.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
*/
99

1010
import * as React from 'react';
11-
import * as ReactDOM from 'react-dom';
12-
import {createRoot} from 'react-dom/client';
11+
import * as ReactDOMClient from 'react-dom/client';
1312
import {
1413
activate as activateBackend,
1514
initialize as initializeBackend,
@@ -41,7 +40,7 @@ function init(appIframe, devtoolsContainer, appSource) {
4140
const DevTools = createDevTools(contentWindow);
4241

4342
inject(contentDocument, appSource, () => {
44-
createRoot(devtoolsContainer).render(
43+
ReactDOMClient.createRoot(devtoolsContainer).render(
4544
<DevTools
4645
hookNamesModuleLoaderFunction={hookNamesModuleLoaderFunction}
4746
showTabBar={true}
@@ -58,4 +57,4 @@ const devtoolsContainer = document.getElementById('devtools');
5857
init(iframe, devtoolsContainer, 'dist/e2e-app.js');
5958

6059
// ReactDOM Test Selector APIs used by Playwright e2e tests
61-
window.parent.REACT_DOM_DEVTOOLS = ReactDOM;
60+
window.parent.REACT_DOM_DEVTOOLS = ReactDOMClient;

packages/react-devtools-shell/webpack-server.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ const makeConfig = (entry, alias) => ({
124124
},
125125
});
126126

127+
const clientAsSeparateBuild = semver.gte(REACT_VERSION, '19.0.0');
128+
127129
const app = makeConfig(
128130
{
129131
'app-index': './src/app/index.js',
@@ -142,8 +144,14 @@ const app = makeConfig(
142144
react: resolve(builtModulesDir, 'react'),
143145
'react-debug-tools': resolve(builtModulesDir, 'react-debug-tools'),
144146
'react-devtools-feature-flags': resolveFeatureFlags('shell'),
145-
'react-dom/client': resolve(builtModulesDir, 'react-dom/client'),
146-
'react-dom': resolve(builtModulesDir, 'react-dom/unstable_testing'),
147+
'react-dom/client': resolve(
148+
builtModulesDir,
149+
clientAsSeparateBuild ? 'react-dom/unstable_testing' : 'react-dom/client',
150+
),
151+
'react-dom': resolve(
152+
builtModulesDir,
153+
clientAsSeparateBuild ? 'react-dom' : 'react-dom/unstable_testing',
154+
),
147155
'react-is': resolve(builtModulesDir, 'react-is'),
148156
scheduler: resolve(builtModulesDir, 'scheduler'),
149157
},

0 commit comments

Comments
 (0)