【问题标题】:Nextjs - 'ReferrenceError: document is not defined' when using useEffectNext Js - 使用 useEffect 时出现“ReferenceError:文档未定义”
【发布时间】:2021-12-15 16:56:59
【问题描述】:

我正在尝试在我的 NextJS 项目中使用 Bootstrap 工具提示。我有其他需要 JS 的引导程序工作,但工具提示需要一些使用 document.querySelectorAll 的 JS,这就是我遇到问题的地方。 bootstrap 文档中的代码是:

var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
  return new bootstrap.Tooltip(tooltipTriggerEl)
})

我已经尝试了我遇到的所有解决方案,例如将代码包装在 typeof document !== "undefined 和使用 useEffect 但似乎在 SSR 期间它仍在尝试运行此代码。我写错了useEffect 还是这里发生了其他事情?这是我的useEffect 代码:

const Page = (props: any) => {

  useEffect(() => {
    var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
    var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
      return new bootstrap.Tooltip(tooltipTriggerEl)
    })
  }, []);

  return (...

我在 NextJS 中使用其余引导程序没有问题,所以请不要只推荐使用 react-bootstrap,我有很多问题。

提前致谢!

以下是使用 `typeof document !== "undefined" 的版本:

if (typeof document !== "undefined") {
    let tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
    let tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
      return new Tooltip(tooltipTriggerEl)
    })
  }

更新:

在 cmets 的帮助下,它现在可以在 useEffect 中运行(因为它在我的代码上没有失败)。但是,它现在对bootstrap.js 中的代码给出了相同的错误。错误如下:

错误 - ReferenceError: 文档未定义 在 enableDismissTrigger (G:\Projects\nextjs\aaip-ts-bs-app\aaip-ts-bs\node_modules\bootstrap\dist\js\bootstrap.js:761:21) 在 G:\Projects\nextjs\aaip-ts-bs-app\aaip-ts-bs\node_modules\bootstrap\dist\js\bootstrap.js:856:3 在 G:\Projects\nextjs\aaip-ts-bs-app\aaip-ts-bs\node_modules\bootstrap\dist\js\bootstrap.js:7:83 在对象。 (G:\Projects\nextjs\aaip-ts-bs-app\aaip-ts-bs\node_modules\bootstrap\dist\js\bootstrap.js:10:3)

所以我猜这是同样的错误,但是当服务器试图在 bootstrap.js 中运行一些代码时?

我尝试在这篇中等帖子中使用next/dynamic 解决方案:Third solution at the bottom of the post,但我不确定如何以这种方式导入Tooltip 并在useEffect 挂钩中使用它。本文使用了一个组件在客户端渲染的例子,本例中没有设置。

如果有帮助,这里是引导程序的 util/component-functions.js 文件中的违规方法:

/**
   * --------------------------------------------------------------------------
   * Bootstrap (v5.1.3): util/component-functions.js
   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
   * --------------------------------------------------------------------------
   */

  const enableDismissTrigger = (component, method = 'hide') => {
    const clickEvent = `click.dismiss${component.EVENT_KEY}`;
    const name = component.NAME;
    EventHandler.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
      if (['A', 'AREA'].includes(this.tagName)) {
        event.preventDefault();
      }

      if (isDisabled(this)) {
        return;
      }

      const target = getElementFromSelector(this) || this.closest(`.${name}`);
      const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method

      instance[method]();
    });
  };

如您所见,在第 4 行它尝试引用 document

感谢 cmets 中的 @juliomalves 帮助我整理原始问题,并提前感谢任何可以帮助我解决新问题的人!

【问题讨论】:

  • 贴出你使用过的代码示例typeof document !== "undefined
  • 嗯,它与上面的相同,但引导代码不是在 useEffect 调用中,而是在 if(typeof document !== "undefined) {... 但我会将它添加到主要问题主体是否有帮助。
  • useEffect 内调用document.querySelectorAll 不会触发您看到的错误。问题很可能来自其他方面。你能提供一个minimal reproducible example吗?
  • 啊,好吧,即使我发誓我的代码没有改变,它现在已经过去了!也许我只需要重新启动调试器实例或其他东西,或者我在某处有错字。谢谢您的帮助。我不再在 useEffect 中看到“未定义文档”,但我现在在 booststrap.js 库中遇到了该错误。我曾尝试使用 next/dynamic 但似乎无法使其正常工作,我已将此问题添加为问题的更新,您能否提供帮助?
  • 我终于把它整理好了,我不知道这是否是一个好习惯,但我会把它作为答案发布,以防其他人有同样的问题。感谢@juliomalves 的帮助

标签: reactjs next.js bootstrap-5


【解决方案1】:

我最初的问题的答案已经在问题的“更新”部分得到解答。

如果有人对第二部分有同样的问题(对 bootstrap.js 中文档的引用),那么这就是我得到的解决方案。我并不是说这是一种好的做法,但它可以按照我的项目的预期编译和工作(我现在有工具提示!)

所以在我最初在useEffect 中导入引导程序的 _app.js 中,我已将其替换为以下内容:

useEffect(() => {
    async function loadBootstrap() {
      const bootstrap = await import("../node_modules/bootstrap/dist/js/bootstrap");

      var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
      var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
        return new bootstrap.Tooltip(tooltipTriggerEl)
      });
    }

    loadBootstrap();
  }, []);

据我所知,因为我们直接调用引导库的某些部分,所以我们需要确保这仅在客户端完成,因此需要在 useEffect 挂钩中进行。为了能够定位bootstrap.Tooltip,我们需要在useEffect 挂钩中导入bootstrap 并将其分配给一个变量。这一切都放在一个异步函数中,该函数被声明然后在useEffect 钩子中调用。

如果您需要任何其他直接或间接引用 document 的引导 js 代码(大部分都这样做),那么也必须在此处输入。

如果您只需要在特定页面而不是 _app.js 上使用它,那么您可以将它移到那里。

【讨论】:

    猜你喜欢
    • 2021-11-21
    • 2021-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-06
    • 2022-01-23
    • 2016-12-14
    • 2022-01-11
    相关资源
    最近更新 更多