diff --git a/packages/docusaurus-theme-common/src/hooks/useSearchPage.ts b/packages/docusaurus-theme-common/src/hooks/useSearchPage.ts
index 1b22512f58..d23a43fefe 100644
--- a/packages/docusaurus-theme-common/src/hooks/useSearchPage.ts
+++ b/packages/docusaurus-theme-common/src/hooks/useSearchPage.ts
@@ -5,32 +5,24 @@
* LICENSE file in the root directory of this source tree.
*/
-import {useCallback, useEffect, useState} from 'react';
-import {useHistory} from '@docusaurus/router';
+import {useCallback} from 'react';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import {useQueryString} from '../utils/historyUtils';
import type {ThemeConfig as AlgoliaThemeConfig} from '@docusaurus/theme-search-algolia';
const SEARCH_PARAM_QUERY = 'q';
-/** Some utility functions around search queries. */
-export function useSearchPage(): {
- /**
- * Works hand-in-hand with `setSearchQuery`; whatever the user has inputted
- * into the search box.
- */
- searchQuery: string;
- /**
- * Set a new search query. In addition to updating `searchQuery`, this handle
- * also mutates the location and appends the query.
- */
- setSearchQuery: (newSearchQuery: string) => void;
- /**
- * Given a query, this handle generates the corresponding search page link,
- * with base URL prepended.
- */
- generateSearchPageLink: (targetSearchQuery: string) => string;
-} {
- const history = useHistory();
+/**
+ * Permits to read/write the current search query string
+ */
+export function useSearchQueryString(): [string, (newValue: string) => void] {
+ return useQueryString(SEARCH_PARAM_QUERY);
+}
+
+/**
+ * Permits to create links to the search page with the appropriate query string
+ */
+export function useSearchLinkCreator(): (searchValue: string) => string {
const {
siteConfig: {baseUrl, themeConfig},
} = useDocusaurusContext();
@@ -38,47 +30,13 @@ export function useSearchPage(): {
algolia: {searchPagePath},
} = themeConfig as AlgoliaThemeConfig;
- const [searchQuery, setSearchQueryState] = useState('');
-
- // Init search query just after React hydration
- useEffect(() => {
- const searchQueryStringValue =
- new URLSearchParams(window.location.search).get(SEARCH_PARAM_QUERY) ?? '';
-
- setSearchQueryState(searchQueryStringValue);
- }, []);
-
- const setSearchQuery = useCallback(
- (newSearchQuery: string) => {
- const searchParams = new URLSearchParams(window.location.search);
-
- if (newSearchQuery) {
- searchParams.set(SEARCH_PARAM_QUERY, newSearchQuery);
- } else {
- searchParams.delete(SEARCH_PARAM_QUERY);
- }
-
- history.replace({
- search: searchParams.toString(),
- });
- setSearchQueryState(newSearchQuery);
- },
- [history],
- );
-
- const generateSearchPageLink = useCallback(
- (targetSearchQuery: string) =>
+ return useCallback(
+ (searchValue: string) =>
// Refer to https://github.com/facebook/docusaurus/pull/2838
// Note: if searchPagePath is falsy, useSearchPage() will not be called
`${baseUrl}${
searchPagePath as string
- }?${SEARCH_PARAM_QUERY}=${encodeURIComponent(targetSearchQuery)}`,
+ }?${SEARCH_PARAM_QUERY}=${encodeURIComponent(searchValue)}`,
[baseUrl, searchPagePath],
);
-
- return {
- searchQuery,
- setSearchQuery,
- generateSearchPageLink,
- };
}
diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts
index 600c75778c..0fdda7f3aa 100644
--- a/packages/docusaurus-theme-common/src/index.ts
+++ b/packages/docusaurus-theme-common/src/index.ts
@@ -73,6 +73,11 @@ export {
type TagLetterEntry,
} from './utils/tagsUtils';
+export {
+ useSearchQueryString,
+ useSearchLinkCreator,
+} from './hooks/useSearchPage';
+
export {isMultiColumnFooterLinks} from './utils/footerUtils';
export {isRegexpStringMatch} from './utils/regexpUtils';
diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts
index 4298d3e985..1814712856 100644
--- a/packages/docusaurus-theme-common/src/internal.ts
+++ b/packages/docusaurus-theme-common/src/internal.ts
@@ -121,7 +121,6 @@ export {
keyboardFocusedClassName,
} from './hooks/useKeyboardNavigation';
export {useLockBodyScroll} from './hooks/useLockBodyScroll';
-export {useSearchPage} from './hooks/useSearchPage';
export {useCodeWordWrap} from './hooks/useCodeWordWrap';
export {getPrismCssVariables} from './utils/codeBlockUtils';
export {useBackToTopButton} from './hooks/useBackToTopButton';
diff --git a/packages/docusaurus-theme-common/src/utils/historyUtils.ts b/packages/docusaurus-theme-common/src/utils/historyUtils.ts
index 06b124a229..686a876f84 100644
--- a/packages/docusaurus-theme-common/src/utils/historyUtils.ts
+++ b/packages/docusaurus-theme-common/src/utils/historyUtils.ts
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
-import {useEffect} from 'react';
+import {useCallback, useEffect} from 'react';
import {useHistory} from '@docusaurus/router';
// @ts-expect-error: TODO temporary until React 18 upgrade
import {useSyncExternalStore} from 'use-sync-external-store/shim';
@@ -75,3 +75,42 @@ export function useQueryStringValue(key: string | null): string | null {
return new URLSearchParams(history.location.search).get(key);
});
}
+
+export function useQueryStringKeySetter(): (
+ key: string,
+ newValue: string | null,
+ options?: {push: boolean},
+) => void {
+ const history = useHistory();
+ return useCallback(
+ (key, newValue, options) => {
+ const searchParams = new URLSearchParams(history.location.search);
+ if (newValue) {
+ searchParams.set(key, newValue);
+ } else {
+ searchParams.delete(key);
+ }
+ const updaterFn = options?.push ? history.push : history.replace;
+ updaterFn({
+ search: searchParams.toString(),
+ });
+ },
+ [history],
+ );
+}
+
+export function useQueryString(
+ key: string,
+): [string, (newValue: string, options?: {push: boolean}) => void] {
+ const value = useQueryStringValue(key) ?? '';
+ const setQueryString = useQueryStringKeySetter();
+ return [
+ value,
+ useCallback(
+ (newValue: string, options) => {
+ setQueryString(key, newValue, options);
+ },
+ [setQueryString, key],
+ ),
+ ];
+}
diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx
index b0456492d4..0f4d7ee000 100644
--- a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx
+++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx
@@ -10,8 +10,10 @@ import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
import Head from '@docusaurus/Head';
import Link from '@docusaurus/Link';
import {useHistory} from '@docusaurus/router';
-import {isRegexpStringMatch} from '@docusaurus/theme-common';
-import {useSearchPage} from '@docusaurus/theme-common/internal';
+import {
+ isRegexpStringMatch,
+ useSearchLinkCreator,
+} from '@docusaurus/theme-common';
import {
useAlgoliaContextualFacetFilters,
useSearchResultUrlProcessor,
@@ -59,10 +61,10 @@ type ResultsFooterProps = {
};
function ResultsFooter({state, onClose}: ResultsFooterProps) {
- const {generateSearchPageLink} = useSearchPage();
+ const createSearchLink = useSearchLinkCreator();
return (
-
+
diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx b/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx
index 1c3db1eb0d..323fb4f458 100644
--- a/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx
+++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx
@@ -21,11 +21,9 @@ import {
HtmlClassNameProvider,
useEvent,
usePluralForm,
+ useSearchQueryString,
} from '@docusaurus/theme-common';
-import {
- useSearchPage,
- useTitleFormatter,
-} from '@docusaurus/theme-common/internal';
+import {useTitleFormatter} from '@docusaurus/theme-common/internal';
import Translate, {translate} from '@docusaurus/Translate';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import {
@@ -167,7 +165,7 @@ function SearchPageContent(): JSX.Element {
const documentsFoundPlural = useDocumentsFoundPlural();
const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
- const {searchQuery, setSearchQuery} = useSearchPage();
+ const [searchQuery, setSearchQuery] = useSearchQueryString();
const initialSearchResultState: ResultDispatcherState = {
items: [],
query: null,