[英]Load Script only once in Next.js using React hooks?
我想在我的博客中使用Utterances 。 它应该只在有人滚动到帖子底部时加载,所以我使用react-intersection-observer
。 我做了以下钩子。
import React from 'react'
import { useTheme } from 'next-themes'
import { siteMetadata } from '@/_data/index'
export const useUtterances = (commentNodeId: string) => {
const config = siteMetadata.comment.utterancesConfig
// username/repo format
const REPO_NAME = config.repo as string
const { theme, resolvedTheme } = useTheme()
const utterancesTheme =
theme === 'light' || resolvedTheme === 'light' ? config.theme : config.darkTheme
React.useEffect(() => {
const scriptParentNode = document.getElementById(commentNodeId)
if (!scriptParentNode) return
// docs - https://utteranc.es/
const script = document.createElement('script')
script.src = 'https://utteranc.es/client.js'
script.async = true
script.setAttribute('repo', REPO_NAME)
script.setAttribute('issue-term', 'pathname')
script.setAttribute('label', 'comment :speech_balloon:')
script.setAttribute('theme', utterancesTheme)
script.setAttribute('crossorigin', 'anonymous')
scriptParentNode.appendChild(script)
return () => {
// cleanup - remove the older script with previous theme
scriptParentNode.removeChild(scriptParentNode.firstChild as Node)
}
}, [REPO_NAME, commentNodeId, utterancesTheme])
}
import React from 'react'
import { useInView } from 'react-intersection-observer'
import { useUtterances } from '@/hooks/useUtterances'
export const Utterances = () => {
const COMMENTS_NODE_ID = 'comments'
const { ref, inView } = useInView({ threshold: 0, triggerOnce: true })
useUtterances(inView ? COMMENTS_NODE_ID : '')
return (
<div ref={ref} className="min-h-[400px]">
{inView ? <div id={COMMENTS_NODE_ID} /> : null}
</div>
)
}
我使用next-themes
来切换DarkMode
。 我还向 utterances iframe 发送了一个请求,因此它不会两次加载脚本,但它仍然通过卸载和安装组件来加载它两次。
import React from 'react'
import { useTheme } from 'next-themes'
import { MoonIcon, SunIcon } from '@heroicons/react/solid'
import { useHasMounted } from '@/hooks/index'
import { siteMetadata } from '@/_data/index'
export const DarkMode = () => {
const { resolvedTheme, setTheme } = useTheme()
const hasMounted = useHasMounted()
const label = resolvedTheme === 'dark' ? 'Activate light mode' : 'Activate dark mode'
if (!hasMounted) return null
const toggleTheme = () => {
const newTheme = resolvedTheme === 'light' ? 'dark' : 'light'
setTheme(newTheme)
// for utterances
const frame = document.getElementsByClassName('utterances-frame')[0] as HTMLIFrameElement
if (frame?.contentWindow) {
const utterancesTheme =
resolvedTheme === 'light'
? siteMetadata.comment.utterancesConfig.darkTheme
: siteMetadata.comment.utterancesConfig.theme
frame.contentWindow.postMessage({ type: 'set-theme', theme: utterancesTheme }, '*')
}
}
return (
<>
<button
className="focus:outline-none"
type="button"
title={label}
aria-label={label}
onClick={toggleTheme}
>
{resolvedTheme === 'light' ? (
<MoonIcon className="w-8 h-8" />
) : (
<SunIcon className="w-8 h-8" />
)}
</button>
</>
)
}
我如何确保它只请求脚本一次? 现在每次我切换时它都会调用它。 它安装和卸载组件,因为我在加载脚本时暂时看不到任何东西。
GitHub 存储库 -> https://github.com/deadcoder0904/next-utterances-script-loads-twice/tree/master
Stackblitz 演示 -> https://stackblitz.com/edit/github-6frqvs?file=pages%2Findex.tsx
在新窗口中打开以查看暗模式,因为 Stackblitz 目前不支持 Tailwind 暗模式。 检查网络选项卡以查看它每次发送请求,即使您还可以看到注释组件安装和卸载。
如何只加载一次脚本?
尝试这样的事情:
React.useEffect(() => {
const scriptParentNode = document.getElementById(commentNodeId)
const utterancesFrame = document.getElementsByClassName('utterances-frame')[0]
if (!scriptParentNode || utterancesFrame) return
// docs - https://utteranc.es/
const script = document.createElement('script')
script.src = 'https://utteranc.es/client.js'
script.async = true
script.setAttribute('repo', REPO_NAME)
script.setAttribute('issue-term', 'pathname')
script.setAttribute('label', 'comment :speech_balloon:')
script.setAttribute('theme', utterancesTheme)
script.setAttribute('crossorigin', 'anonymous')
scriptParentNode.appendChild(script)
}, [REPO_NAME, commentNodeId, utterancesTheme])
所以这里基本上发生了什么。 看起来当我们在加载后附加 utterances 脚本时,它自己替换为:
<div class="utterances" style="height: 267px;">
<iframe class="utterances-frame" title="Comments" scrolling="no" src="https://utteranc.es/..." loading="lazy"></iframe>
</div>
为了避免不必要的追加,我们检查页面中是否已经存在一些<iframe class="utterances-frame"
并且仅当没有追加脚本时。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.