[英]Load Script only once in Next.js using React hooks?
I want to use Utterances for my blog.我想在我的博客中使用Utterances 。 It should only load when someone scrolled to the bottom of the post so I use
react-intersection-observer
for that.它应该只在有人滚动到帖子底部时加载,所以我使用
react-intersection-observer
。 I have made the following hook.我做了以下钩子。
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>
)
}
I use next-themes
to toggle DarkMode
.我使用
next-themes
来切换DarkMode
。 I also send a request to utterances iframe so it doesn't load script twice but it still loads it twice by unmounting & mounting the component.我还向 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>
</>
)
}
How do I make sure it only requests script once?我如何确保它只请求脚本一次? It now calls it everytime I toggle.
现在每次我切换时它都会调用它。 It mounts and unmounts the component as I see nothing for a while when the script is loading.
它安装和卸载组件,因为我在加载脚本时暂时看不到任何东西。
GitHub repo -> https://github.com/deadcoder0904/next-utterances-script-loads-twice/tree/master GitHub 存储库 -> https://github.com/deadcoder0904/next-utterances-script-loads-twice/tree/master
Stackblitz demo -> https://stackblitz.com/edit/github-6frqvs?file=pages%2Findex.tsx Stackblitz 演示 -> https://stackblitz.com/edit/github-6frqvs?file=pages%2Findex.tsx
Open in New Window to see the Dark Mode as Stackblitz currently doesn't support Tailwind Dark Mode.在新窗口中打开以查看暗模式,因为 Stackblitz 目前不支持 Tailwind 暗模式。 Check the Network Tab to see it sends request everytime even though you can also see the Comments component mounting & unmounting.
检查网络选项卡以查看它每次发送请求,即使您还可以看到注释组件安装和卸载。
How do I only load script once?如何只加载一次脚本?
Try something like this:尝试这样的事情:
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])
So what basically is happen here.所以这里基本上发生了什么。 It is looks like when we append the utterances script after loading it replaces himself with:
看起来当我们在加载后附加 utterances 脚本时,它自己替换为:
<div class="utterances" style="height: 267px;">
<iframe class="utterances-frame" title="Comments" scrolling="no" src="https://utteranc.es/..." loading="lazy"></iframe>
</div>
To avoid unnecessary append we check if there are some <iframe class="utterances-frame"
already in the page and only if not append the script.为了避免不必要的追加,我们检查页面中是否已经存在一些
<iframe class="utterances-frame"
并且仅当没有追加脚本时。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.