Skip to content

Commit fd1c56e

Browse files
next/script fix duplicate scripts (#28428)
* Fix #27747 * Fix lint error * Add data attribute to script component * Fix #28036 * Fix tests * Fix tests Co-authored-by: JJ Kasper <jj@jjsweb.site>
1 parent ee46244 commit fd1c56e

File tree

5 files changed

+26
-8
lines changed

5 files changed

+26
-8
lines changed

packages/next/client/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,17 @@ window.__NEXT_P = []
156156
const headManager: {
157157
mountedInstances: Set<unknown>
158158
updateHead: (head: JSX.Element[]) => void
159+
getIsSsr?: () => boolean
159160
} = initHeadManager()
160161
const appElement: HTMLElement | null = document.getElementById('__next')
161162

162163
let lastRenderReject: (() => void) | null
163164
let webpackHMR: any
164165
export let router: Router
165166
let CachedApp: AppComponent, onPerfEntry: (metric: any) => void
167+
headManager.getIsSsr = () => {
168+
return router.isSsr
169+
}
166170

167171
class Container extends React.Component<{
168172
fn: (err: Error, info?: any) => void

packages/next/client/script.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ function Script(props: ScriptProps): JSX.Element | null {
140140
} = props
141141

142142
// Context is available only during SSR
143-
const { updateScripts, scripts } = useContext(HeadManagerContext)
143+
const { updateScripts, scripts, getIsSsr } = useContext(HeadManagerContext)
144144

145145
useEffect(() => {
146146
if (strategy === 'afterInteractive') {
@@ -161,7 +161,10 @@ function Script(props: ScriptProps): JSX.Element | null {
161161
},
162162
])
163163
updateScripts(scripts)
164-
} else {
164+
} else if (getIsSsr && getIsSsr()) {
165+
// Script has already loaded during SSR
166+
LoadCache.add(restProps.id || src)
167+
} else if (getIsSsr && !getIsSsr()) {
165168
loadScript(props)
166169
}
167170
}

packages/next/shared/lib/head-manager-context.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const HeadManagerContext: React.Context<{
55
mountedInstances?: any
66
updateScripts?: (state: any) => void
77
scripts?: any
8+
getIsSsr?: () => boolean
89
}> = React.createContext({})
910

1011
if (process.env.NODE_ENV !== 'production') {

test/integration/script-loader/pages/page3.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ const Page = () => {
44
return (
55
<div class="container">
66
<Script id="inline-script">
7-
{`(window.onload = function () {
8-
const newDiv = document.createElement('div')
9-
newDiv.id = 'onload-div'
10-
document.querySelector('body').appendChild(newDiv)
11-
})`}
7+
{`const newDiv = document.createElement('div')
8+
newDiv.id = 'onload-div'
9+
document.querySelector('body').appendChild(newDiv)
10+
`}
1211
</Script>
1312
<Script
1413
id="scriptLazyOnload"

test/integration/script-loader/test/index.test.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,25 @@ describe('Script Loader', () => {
128128
try {
129129
browser = await webdriver(appPort, '/')
130130

131+
// beforeInteractive scripts should load once
132+
let documentBIScripts = await browser.elementsByCss(
133+
'[src$="documentBeforeInteractive"]'
134+
)
135+
expect(documentBIScripts.length).toBe(1)
136+
131137
await browser.waitForElementByCss('[href="/page1"]')
132138
await browser.click('[href="/page1"]')
133139

134140
await browser.waitForElementByCss('.container')
135-
await waitFor(1000)
136141

137142
const script = await browser.elementById('scriptBeforeInteractive')
138143

144+
// Ensure beforeInteractive script isn't duplicated on navigation
145+
documentBIScripts = await browser.elementsByCss(
146+
'[src$="documentBeforeInteractive"]'
147+
)
148+
expect(documentBIScripts.length).toBe(1)
149+
139150
// Renders script tag
140151
expect(script).toBeDefined()
141152
} finally {

0 commit comments

Comments
 (0)