Fix(docs): prevent SecurityError from rapid history.replaceState call… (#38672)

* Fix(docs): prevent SecurityError from rapid history.replaceState calls during fast scrolling

* url now chases the scroll

* fixing merge conflicts

* refactor: minor changes to safeHistoryReplaceState

---------

Co-authored-by: Charis <26616127+charislam@users.noreply.github.com>
This commit is contained in:
Devanshu Sharma
2025-09-19 02:30:10 +05:30
committed by GitHub
parent 789af2d535
commit 0674343912
5 changed files with 21 additions and 4 deletions

View File

@@ -8,6 +8,7 @@ import MessageBird from './MessageBirdConfig.mdx'
import Twilio from './TwilioConfig.mdx'
import Vonage from './VonageConfig.mdx'
import TextLocal from './TextLocalConfig.mdx'
import { safeHistoryReplaceState } from '~/lib/historyUtils'
const reducer = (_, action: (typeof PhoneLoginsItems)[number] | undefined) => {
const url = new URL(document.location.href)
@@ -16,7 +17,7 @@ const reducer = (_, action: (typeof PhoneLoginsItems)[number] | undefined) => {
} else {
url.searchParams.delete('showSmsProvider')
}
window.history.replaceState(null, '', url)
safeHistoryReplaceState(url.toString())
return action
}

View File

@@ -3,6 +3,7 @@
import type { HTMLAttributes, PropsWithChildren } from 'react'
import { useContext, useEffect, useRef, useState } from 'react'
import { useInView } from 'react-intersection-observer'
import { safeHistoryReplaceState } from '~/lib/historyUtils'
import {
cn,
@@ -44,7 +45,7 @@ export function ReferenceSectionWrapper({
initialScrollHappened &&
window.scrollY > 0 /* Don't update on first navigation to introduction */
) {
window.history.replaceState(null, '', link)
safeHistoryReplaceState(link)
}
},
})

View File

@@ -6,6 +6,7 @@ import { useNavigationMenuContext } from '~/components/Navigation/NavigationMenu
import { menuState } from '~/hooks/useMenuState'
import Image from 'next/legacy/image'
import { cn } from 'ui'
import { safeHistoryReplaceState } from '~/lib/historyUtils'
interface ISectionContainer {
id: string
@@ -97,7 +98,7 @@ const StickyHeader: FC<StickyHeader> = ({ icon, ...props }) => {
onChange: (inView, entry) => {
if (inView && window) highlightSelectedNavItem(entry.target.attributes['data-ref-id'].value)
if (inView && props.scrollSpyHeader) {
window.history.replaceState(null, '', entry.target.id)
safeHistoryReplaceState(entry.target.id)
// if (setActiveRefItem) setActiveRefItem(entry.target.attributes['data-ref-id'].value)
menuState.setMenuActiveRefId(entry.target.attributes['data-ref-id'].value)
// router.push(`/reference/javascript/${entry.target.attributes['data-ref-id'].value}`, null, {

View File

@@ -4,6 +4,7 @@ import { highlightSelectedNavItem } from 'ui/src/components/CustomHTMLElements/C
import { useRouter } from 'next/compat/router'
import { useNavigationMenuContext } from '~/components/Navigation/NavigationMenu/NavigationMenu.Context'
import { menuState } from '~/hooks/useMenuState'
import { safeHistoryReplaceState } from '~/lib/historyUtils'
interface ISectionContainer {
id: string
@@ -60,7 +61,7 @@ const StickyHeader: FC<StickyHeader> = (props) => {
onChange: (inView, entry) => {
if (inView && window) highlightSelectedNavItem(entry.target.attributes['data-ref-id'].value)
if (inView && props.scrollSpyHeader) {
window.history.replaceState(null, '', entry.target.id)
safeHistoryReplaceState(entry.target.id)
// if (setActiveRefItem) setActiveRefItem(entry.target.attributes['data-ref-id'].value)
menuState.setMenuActiveRefId(entry.target.attributes['data-ref-id'].value)
// router.push(`/reference/javascript/${entry.target.attributes['data-ref-id'].value}`, null, {

View File

@@ -0,0 +1,13 @@
import { debounce } from 'lodash-es'
export const safeHistoryReplaceState = debounce((url: string) => {
if (typeof window === 'undefined') return
if (url === window.location.href) return
try {
window.history.replaceState(null, '', url)
} catch (error) {
console.warn('Failed to call history.replaceState:', error)
}
}, 120)