Skip to content

Commit 2d8281f

Browse files
authored
fix(core): do not recreate ReactDOM Root, fix React warning on hot reload (#10103)
1 parent 4159b25 commit 2d8281f

File tree

4 files changed

+62
-50
lines changed

4 files changed

+62
-50
lines changed

packages/docusaurus-module-type-aliases/src/index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -387,4 +387,5 @@ interface Window {
387387
prefetch: (url: string) => false | Promise<void[]>;
388388
preload: (url: string) => false | Promise<void[]>;
389389
};
390+
docusaurusRoot?: import('react-dom/client').Root;
390391
}

packages/docusaurus/src/client/clientEntry.tsx

+12-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import React from 'react';
8+
import React, {startTransition} from 'react';
99
import ReactDOM, {type ErrorInfo} from 'react-dom/client';
1010
import {BrowserRouter} from 'react-router-dom';
1111
import {HelmetProvider} from 'react-helmet-async';
@@ -46,21 +46,24 @@ if (ExecutionEnvironment.canUseDOM) {
4646
};
4747

4848
const renderApp = () => {
49+
if (window.docusaurusRoot) {
50+
window.docusaurusRoot.render(app);
51+
return;
52+
}
4953
if (hydrate) {
50-
React.startTransition(() => {
51-
ReactDOM.hydrateRoot(container, app, {
52-
onRecoverableError,
53-
});
54+
window.docusaurusRoot = ReactDOM.hydrateRoot(container, app, {
55+
onRecoverableError,
5456
});
5557
} else {
5658
const root = ReactDOM.createRoot(container, {onRecoverableError});
57-
React.startTransition(() => {
58-
root.render(app);
59-
});
59+
root.render(app);
60+
window.docusaurusRoot = root;
6061
}
6162
};
6263

63-
preload(window.location.pathname).then(renderApp);
64+
preload(window.location.pathname).then(() => {
65+
startTransition(renderApp);
66+
});
6467

6568
// Webpack Hot Module Replacement API
6669
if (module.hot) {

packages/docusaurus/src/client/docusaurus.ts

+49-40
Original file line numberDiff line numberDiff line change
@@ -45,46 +45,55 @@ const getChunkNamesToLoad = (path: string): string[] =>
4545
)
4646
.flatMap(([, routeChunks]) => Object.values(flat(routeChunks)));
4747

48-
const docusaurus = {
49-
prefetch(routePath: string): false | Promise<void[]> {
50-
if (!canPrefetch(routePath)) {
51-
return false;
52-
}
53-
fetched.add(routePath);
54-
55-
// Find all webpack chunk names needed.
56-
const matches = matchRoutes(routes, routePath);
57-
58-
const chunkNamesNeeded = matches.flatMap((match) =>
59-
getChunkNamesToLoad(match.route.path),
60-
);
61-
62-
// Prefetch all webpack chunk assets file needed.
63-
return Promise.all(
64-
chunkNamesNeeded.map((chunkName) => {
65-
// "__webpack_require__.gca" is injected by ChunkAssetPlugin. Pass it
66-
// the name of the chunk you want to load and it will return its URL.
67-
// eslint-disable-next-line camelcase
68-
const chunkAsset = __webpack_require__.gca(chunkName);
69-
70-
// In some cases, webpack might decide to optimize further, leading to
71-
// the chunk assets being merged to another chunk. In this case, we can
72-
// safely filter it out and don't need to load it.
73-
if (chunkAsset && !chunkAsset.includes('undefined')) {
74-
return prefetchHelper(chunkAsset);
75-
}
76-
return Promise.resolve();
77-
}),
78-
);
79-
},
80-
81-
preload(routePath: string): false | Promise<void[]> {
82-
if (!canPreload(routePath)) {
83-
return false;
84-
}
85-
loaded.add(routePath);
86-
return preloadHelper(routePath);
87-
},
48+
type Docusaurus = Window['docusaurus'];
49+
50+
const prefetch: Docusaurus['prefetch'] = (
51+
routePath: string,
52+
): false | Promise<void[]> => {
53+
if (!canPrefetch(routePath)) {
54+
return false;
55+
}
56+
fetched.add(routePath);
57+
58+
// Find all webpack chunk names needed.
59+
const matches = matchRoutes(routes, routePath);
60+
61+
const chunkNamesNeeded = matches.flatMap((match) =>
62+
getChunkNamesToLoad(match.route.path),
63+
);
64+
65+
// Prefetch all webpack chunk assets file needed.
66+
return Promise.all(
67+
chunkNamesNeeded.map((chunkName) => {
68+
// "__webpack_require__.gca" is injected by ChunkAssetPlugin. Pass it
69+
// the name of the chunk you want to load and it will return its URL.
70+
// eslint-disable-next-line camelcase
71+
const chunkAsset = __webpack_require__.gca(chunkName);
72+
73+
// In some cases, webpack might decide to optimize further, leading to
74+
// the chunk assets being merged to another chunk. In this case, we can
75+
// safely filter it out and don't need to load it.
76+
if (chunkAsset && !chunkAsset.includes('undefined')) {
77+
return prefetchHelper(chunkAsset);
78+
}
79+
return Promise.resolve();
80+
}),
81+
);
82+
};
83+
84+
const preload: Docusaurus['preload'] = (
85+
routePath: string,
86+
): false | Promise<void[]> => {
87+
if (!canPreload(routePath)) {
88+
return false;
89+
}
90+
loaded.add(routePath);
91+
return preloadHelper(routePath);
92+
};
93+
94+
const docusaurus: Window['docusaurus'] = {
95+
prefetch,
96+
preload,
8897
};
8998

9099
// This object is directly mounted onto window, better freeze it

project-words.txt

-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ datagit
6767
Datagit
6868
Datagit's
6969
dedup
70-
Déja
7170
devto
7271
dingers
7372
Dmitry

0 commit comments

Comments
 (0)