我想在我的博客上使用Utterances。它应该只在有人滚动到帖子底部时加载,所以我使用react-intersection-observer。我做了下面的钩子。
useUtterances.ts
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])
}Utterances.tsx
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发送了一个请求,这样它就不会加载两次脚本,但它仍然会通过卸载和安装组件来加载两次脚本。
DarkMode.tsx
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 repo -> https://github.com/deadcoder0904/next-utterances-script-loads-twice/tree/master
Stackblitz演示-> https://stackblitz.com/edit/github-6frqvs?file=pages%2Findex.tsx
在新窗口中打开以查看暗模式,因为Stackblitz当前不支持尾风暗模式。检查Network选项卡,以查看它每次都发送请求,即使您还可以看到注释组件安装和卸载。
如何只加载脚本一次?
发布于 2021-07-28 08:51:08
尝试如下所示:
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])所以这里基本上发生了什么。它看起来像是,当我们在加载后附加话语脚本时,它将自己替换为:
<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",并且只有在没有附加脚本的情况下才检查。
https://stackoverflow.com/questions/68554620
复制相似问题