繁体   English   中英

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

[英]Load Script only once in Next.js using React hooks?

我想在我的博客中使用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])
}

话语.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 发送了一个请求,因此它不会两次加载脚本,但它仍然通过卸载和安装组件来加载它两次。

暗模式.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 存储库 -> 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM