diff --git a/src/providers/TableOfContentsProvider.tsx b/src/providers/TableOfContentsProvider.tsx index 8bf3b701a..2030b2434 100644 --- a/src/providers/TableOfContentsProvider.tsx +++ b/src/providers/TableOfContentsProvider.tsx @@ -51,7 +51,6 @@ export interface TableOfContentsProviderProps { children: ReactNode; hashNavigation?: boolean; } - export const TableOfContentsProvider: FC = ({ items, children, @@ -62,6 +61,7 @@ export const TableOfContentsProvider: FC = ({ const [selected, setSelected] = useState(items[0]?.value); const [elements, setElements] = useState<(Element | null)[]>([]); const [shouldInstantlyJump, setShouldInstantlyJump] = useState(hash !== ''); + const initHashStateRef = useRef(false); const values: string[] = useMemo( () => items.flatMap((item) => getValues([], item)), @@ -73,6 +73,11 @@ export const TableOfContentsProvider: FC = ({ setElements(values.map((value) => document.getElementById(value))); }, [values]); + useEffect(() => { + if (hash.length) return; + initHashStateRef.current = true; + }, [hash]); + const isActive = useCallback( (item: TableOfContentsItemType) => { if (item.value === selected) return true; @@ -154,12 +159,30 @@ export const TableOfContentsProvider: FC = ({ newSelectedIndex = index; } } - if (newSelectedIndex !== -1 && values.at(newSelectedIndex) !== undefined) { - setSelected(values[newSelectedIndex]); - if (hashNavigation) { - navigate(`#${values[newSelectedIndex]}`, { replace: true }); - } + + if (newSelectedIndex === -1 || values.at(newSelectedIndex) === undefined) + return; + + const targetValue = hash.replace('#', ''); + if ( + !initHashStateRef.current && + targetValue.length && + values.includes(targetValue) + ) { + handleSetSelected(targetValue, { + behavior: 'instant', + shouldInstantlyJumpOnMount: true, + }); + initHashStateRef.current = true; + return; + } + + setSelected(values[newSelectedIndex]); + if (hashNavigation) { + navigate(`#${values[newSelectedIndex]}`, { replace: true }); } + // this effect handles scroll navigation and should not be triggered on hash change + // eslint-disable-next-line react-hooks/exhaustive-deps }, [hashNavigation, navigate, values, visible]); /* v8 ignore end */