首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用React钩子在Next.js中只加载脚本一次?

使用React钩子在Next.js中只加载脚本一次?
EN

Stack Overflow用户
提问于 2021-07-28 12:59:28
回答 1查看 176关注 0票数 0

我想在我的博客上使用Utterances。它应该只在有人滚动到帖子底部时加载,所以我使用react-intersection-observer。我做了下面的钩子。

useUtterances.ts

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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选项卡,以查看它每次都发送请求,即使您还可以看到注释组件安装和卸载。

如何只加载脚本一次?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-07-28 08:51:08

尝试如下所示:

代码语言:javascript
复制
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])

所以这里基本上发生了什么。它看起来像是,当我们在加载后附加话语脚本时,它将自己替换为:

代码语言:javascript
复制
<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",并且只有在没有附加脚本的情况下才检查。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68554620

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档