From 8bda3b2dbf071f214462eaec751c914f64c8f029 Mon Sep 17 00:00:00 2001 From: Alexey Pyltsyn Date: Tue, 22 Jun 2021 13:41:58 +0300 Subject: [PATCH] fix(v2): ignore hash changes in useChangeRoute hook (#5023) * fix(v2): ignore hash changes in useChangeRoute hook * refactor and introduce useLocationChange hook Co-authored-by: slorber --- .../src/index.d.ts | 4 ++ .../src/theme/DocSidebar/index.tsx | 14 +++---- .../src/theme/SkipToContent/index.tsx | 6 +-- .../src/theme/hooks/useHideableNavbar.ts | 6 +-- packages/docusaurus-theme-common/src/index.ts | 4 +- .../src/utils/useChangeRoute.ts | 21 ----------- .../src/utils/useLocationChange.ts | 37 +++++++++++++++++++ .../src/utils/usePrevious.ts | 18 +++++++++ 8 files changed, 73 insertions(+), 37 deletions(-) delete mode 100644 packages/docusaurus-theme-common/src/utils/useChangeRoute.ts create mode 100644 packages/docusaurus-theme-common/src/utils/useLocationChange.ts create mode 100644 packages/docusaurus-theme-common/src/utils/usePrevious.ts diff --git a/packages/docusaurus-module-type-aliases/src/index.d.ts b/packages/docusaurus-module-type-aliases/src/index.d.ts index e44eefb76e..d195eafd08 100644 --- a/packages/docusaurus-module-type-aliases/src/index.d.ts +++ b/packages/docusaurus-module-type-aliases/src/index.d.ts @@ -167,6 +167,10 @@ declare module '@docusaurus/router' { // eslint-disable-next-line import/no-extraneous-dependencies export * from 'react-router-dom'; } +declare module '@docusaurus/history' { + // eslint-disable-next-line import/no-extraneous-dependencies + export * from 'history'; +} declare module '@docusaurus/useDocusaurusContext' { export default function (): any; diff --git a/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx index 1e549c9fa3..e5b3251b6d 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx @@ -7,7 +7,11 @@ import React, {useState, useCallback, useEffect, useRef, memo} from 'react'; import clsx from 'clsx'; -import {useThemeConfig, isSamePath} from '@docusaurus/theme-common'; +import { + useThemeConfig, + isSamePath, + usePrevious, +} from '@docusaurus/theme-common'; import useUserPreferencesContext from '@theme/hooks/useUserPreferencesContext'; import useLockBodyScroll from '@theme/hooks/useLockBodyScroll'; import useWindowSize, {windowSizes} from '@theme/hooks/useWindowSize'; @@ -25,14 +29,6 @@ import styles from './styles.module.css'; const MOBILE_TOGGLE_SIZE = 24; -function usePrevious(value) { - const ref = useRef(value); - useEffect(() => { - ref.current = value; - }, [value]); - return ref.current; -} - const isActiveSidebarItem = (item, activePath) => { if (item.type === 'link') { return isSamePath(item.href, activePath); diff --git a/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx b/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx index 082cd76bb7..5f8fa92637 100644 --- a/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx @@ -8,7 +8,7 @@ import React, {useRef} from 'react'; import clsx from 'clsx'; import Translate from '@docusaurus/Translate'; -import {useChangeRoute} from '@docusaurus/theme-common'; +import {useLocationChange} from '@docusaurus/theme-common'; import styles from './styles.module.css'; @@ -32,8 +32,8 @@ function SkipToContent(): JSX.Element { } }; - useChangeRoute(() => { - if (containerRef.current) { + useLocationChange(({location}) => { + if (containerRef.current && !location.hash) { programmaticFocus(containerRef.current); } }); diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.ts b/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.ts index ec840f7951..c83949982a 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.ts +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.ts @@ -8,7 +8,7 @@ import {useState, useCallback, useEffect, useRef} from 'react'; import {useLocation} from '@docusaurus/router'; import useScrollPosition from '@theme/hooks/useScrollPosition'; -import {useChangeRoute} from '@docusaurus/theme-common'; +import {useLocationChange} from '@docusaurus/theme-common'; import type {useHideableNavbarReturns} from '@theme/hooks/useHideableNavbar'; const useHideableNavbar = (hideOnScroll: boolean): useHideableNavbarReturns => { @@ -56,8 +56,8 @@ const useHideableNavbar = (hideOnScroll: boolean): useHideableNavbarReturns => { [navbarHeight, isFocusedAnchor], ); - useChangeRoute(() => { - if (!hideOnScroll) { + useLocationChange((locationChangeEvent) => { + if (!hideOnScroll || locationChangeEvent.location.hash) { return; } diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 4411fd5d0b..840065058b 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -33,7 +33,9 @@ export {useTitleFormatter} from './utils/generalUtils'; export {usePluralForm} from './utils/usePluralForm'; -export {useChangeRoute} from './utils/useChangeRoute'; +export {useLocationChange} from './utils/useLocationChange'; + +export {usePrevious} from './utils/usePrevious'; export { useDocsPreferredVersion, diff --git a/packages/docusaurus-theme-common/src/utils/useChangeRoute.ts b/packages/docusaurus-theme-common/src/utils/useChangeRoute.ts deleted file mode 100644 index 97ac26c87d..0000000000 --- a/packages/docusaurus-theme-common/src/utils/useChangeRoute.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import {useRef, useEffect} from 'react'; -import {useLocation} from '@docusaurus/router'; - -export function useChangeRoute(onRouteChange: () => void): void { - const {pathname} = useLocation(); - const latestPathnameRef = useRef(pathname); - - useEffect(() => { - if (pathname !== latestPathnameRef.current) { - latestPathnameRef.current = pathname; - onRouteChange(); - } - }, [pathname, latestPathnameRef, onRouteChange]); -} diff --git a/packages/docusaurus-theme-common/src/utils/useLocationChange.ts b/packages/docusaurus-theme-common/src/utils/useLocationChange.ts new file mode 100644 index 0000000000..e230321296 --- /dev/null +++ b/packages/docusaurus-theme-common/src/utils/useLocationChange.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {useEffect, useRef} from 'react'; +import {useLocation} from '@docusaurus/router'; +import {Location} from '@docusaurus/history'; +import {usePrevious} from './usePrevious'; + +type LocationChangeEvent = { + location: Location; + previousLocation: Location | undefined; +}; + +type OnLocationChange = (locationChangeEvent: LocationChangeEvent) => void; + +export function useLocationChange(onLocationChange: OnLocationChange): void { + const location = useLocation(); + const previousLocation = usePrevious(location); + const isFirst = useRef(true); + + useEffect(() => { + // Prevent first effect to trigger the listener on mount + if (isFirst.current) { + isFirst.current = false; + return; + } + + onLocationChange({ + location, + previousLocation, + }); + }, [location]); +} diff --git a/packages/docusaurus-theme-common/src/utils/usePrevious.ts b/packages/docusaurus-theme-common/src/utils/usePrevious.ts new file mode 100644 index 0000000000..2a27462ff3 --- /dev/null +++ b/packages/docusaurus-theme-common/src/utils/usePrevious.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {useRef, useEffect} from 'react'; + +export function usePrevious(value: T): T | undefined { + const ref = useRef(); + + useEffect(() => { + ref.current = value; + }); + + return ref.current; +}