From 6cb955987e510e3b98250d3841a94fee2b229ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lorber?= Date: Fri, 13 Jun 2025 18:49:28 +0200 Subject: [PATCH] fix(theme): make `useHistorySelector()` hydration-safe + use it read search/hash in theme (#11263) --- argos/tests/screenshot.spec.ts | 5 ----- .../NavbarItem/DocsVersionDropdownNavbarItem.tsx | 5 +++-- .../NavbarItem/LocaleDropdownNavbarItem/index.tsx | 5 +++-- .../src/utils/historyUtils.ts | 13 ++++++++++++- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/argos/tests/screenshot.spec.ts b/argos/tests/screenshot.spec.ts index d515fc0f33..9ceaf5ad8c 100644 --- a/argos/tests/screenshot.spec.ts +++ b/argos/tests/screenshot.spec.ts @@ -134,11 +134,6 @@ function throwOnConsole(page: Page) { // it's already happening in main branch 'Failed to load resource: the server responded with a status of 404 (Not Found)', - // TODO legit hydration bugs to fix on embeds of /docs/styling-layout - // useLocation() returns window.search/hash immediately :s - '/docs/configuration?docusaurus-theme=light', - '/docs/configuration?docusaurus-theme=dark', - // Warning because react-live not supporting React automatic JSX runtime // See https://github.com/FormidableLabs/react-live/issues/405 'Your app (or one of its dependencies) is using an outdated JSX transform. Update to the modern JSX transform for faster performance', diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index 1e2d0bbf7a..fdd28ebc25 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -13,7 +13,7 @@ import { useDocsPreferredVersion, } from '@docusaurus/plugin-content-docs/client'; import {translate} from '@docusaurus/Translate'; -import {useLocation} from '@docusaurus/router'; +import {useHistorySelector} from '@docusaurus/theme-common'; import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem'; import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem'; import type { @@ -119,7 +119,8 @@ export default function DocsVersionDropdownNavbarItem({ versions: configs, ...props }: Props): ReactNode { - const {search, hash} = useLocation(); + const search = useHistorySelector((history) => history.location.search); + const hash = useHistorySelector((history) => history.location.hash); const activeDocContext = useActiveDocContext(docsPluginId); const {savePreferredVersionName} = useDocsPreferredVersion(docsPluginId); const versionItems = useVersionItems({docsPluginId, configs}); diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/LocaleDropdownNavbarItem/index.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/LocaleDropdownNavbarItem/index.tsx index 66dec60797..21b14d417e 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/LocaleDropdownNavbarItem/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/LocaleDropdownNavbarItem/index.tsx @@ -9,7 +9,7 @@ import React, {type ReactNode} from 'react'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import {useAlternatePageUtils} from '@docusaurus/theme-common/internal'; import {translate} from '@docusaurus/Translate'; -import {useLocation} from '@docusaurus/router'; +import {useHistorySelector} from '@docusaurus/theme-common'; import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem'; import IconLanguage from '@theme/Icon/Language'; import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem'; @@ -28,7 +28,8 @@ export default function LocaleDropdownNavbarItem({ i18n: {currentLocale, locales, localeConfigs}, } = useDocusaurusContext(); const alternatePageUtils = useAlternatePageUtils(); - const {search, hash} = useLocation(); + const search = useHistorySelector((history) => history.location.search); + const hash = useHistorySelector((history) => history.location.hash); const localeItems = locales.map((locale): LinkLikeNavbarItemProps => { const baseTo = `pathname://${alternatePageUtils.createUrl({ diff --git a/packages/docusaurus-theme-common/src/utils/historyUtils.ts b/packages/docusaurus-theme-common/src/utils/historyUtils.ts index 4695896a1e..eea45351d8 100644 --- a/packages/docusaurus-theme-common/src/utils/historyUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/historyUtils.ts @@ -57,7 +57,18 @@ export function useHistorySelector( return useSyncExternalStore( history.listen, () => selector(history), - () => selector(history), + () => + selector({ + ...history, + location: { + ...history.location, + // On the server/hydration, these attributes should always be empty + // Forcing empty state makes this hook safe from hydration errors + search: '', + hash: '', + state: undefined, + }, + }), ); }