简体   繁体   English

Nextjs - 使用 useEffect 时出现“ReferrenceError:文档未定义”

[英]Nextjs - 'ReferrenceError: document is not defined' when using useEffect

I'm trying to get Bootstrap tooltips working in my NextJS project.我正在尝试让 Bootstrap 工具提示在我的 NextJS 项目中工作。 I've got other parts of bootstrap working that require JS but tooltips need a bit of JS that uses document.querySelectorAll and this is where I am having the issue.我有需要 JS 的引导程序的其他部分工作,但工具提示需要一些使用document.querySelectorAll的 JS,这就是我遇到问题的地方。 The code from bootstrap's docs is: bootstrap 文档中的代码是:

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

I have tried all the solutions I've come across, such as wrapping the code in typeof document !== "undefined and using useEffect but it seems that during SSR it's still trying to run this code. Have I written the useEffect incorrectly or is something else going on here? Here is my useEffect code:我已经尝试过我遇到的所有解决方案,例如将代码包装在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 (...

I am using the rest of bootstrap in NextJS without issues so please don't just recommend using react-bootstrap, I had a lot of issues with that.我在 NextJS 中使用引导程序的 rest 没有问题,所以请不要只推荐使用 react-bootstrap,我有很多问题。

Thanks in advance!提前致谢!

Here are the versions using `typeof document:== "undefined":以下是使用 `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)
    })
  }

UPDATE:更新:

With some help from the comments, it's working now inside the useEffect (as in it's not failing on my code).在评论的帮助下,它现在在 useEffect 中工作(因为它没有在我的代码上失败)。 However, it's now giving the same error for code within bootstrap.js .但是,它现在对bootstrap.js中的代码给出了相同的错误。 The error is as follows:错误如下:

error - ReferenceError: document is not defined at enableDismissTrigger (G:\Projects\nextjs\aaip-ts-bs-app\aaip-ts-bs\node_modules\bootstrap\dist\js\bootstrap.js:761:21) at G:\Projects\nextjs\aaip-ts-bs-app\aaip-ts-bs\node_modules\bootstrap\dist\js\bootstrap.js:856:3 at G:\Projects\nextjs\aaip-ts-bs-app\aaip-ts-bs\node_modules\bootstrap\dist\js\bootstrap.js:7:83 at Object.错误 - ReferenceError: 文档未在 G 的 enableDismissTrigger (G:\Projects\nextjs\aaip-ts-bs\node_modules\bootstrap\dist\js\bootstrap.js:761:21) 处定义:\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 在 Object。 (G:\Projects\nextjs\aaip-ts-bs-app\aaip-ts-bs\node_modules\bootstrap\dist\js\bootstrap.js:10:3) (G:\Projects\nextjs\aaip-ts-bs-app\aaip-ts-bs\node_modules\bootstrap\dist\js\bootstrap.js:10:3)

So I guess it's the same error but when the server is trying to run some code within bootstrap.js?所以我想这是同样的错误,但是当服务器试图在 bootstrap.js 中运行一些代码时?

I've tried to use the next/dynamic solution in this medium post: Third solution at the bottom of the post but I'm not really sure how to import Tooltip that way and use it inside the useEffect hook.我尝试在这篇中等帖子中使用next/dynamic解决方案:帖子底部的第三个解决方案,但我不确定如何以这种方式导入Tooltip并在useEffect挂钩中使用它。 The article uses the example of a component to be rendered on the client, which isn't the setup in this case.本文使用要在客户端呈现的组件的示例,本例中没有设置。

Here is the offending method inside bootstrap's util/component-functions.js file if it's helpful:如果有帮助,这是引导程序的 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]();
    });
  };

As you can see, on the 4th line it tries to reference document .如您所见,在第 4 行它尝试引用document

Thanks to @juliomalves in the comments for helping me sort the original issue and thanks in advance to anyone who can help me with the new one!感谢评论中的@juliomalves 帮助我对原始问题进行分类,并提前感谢任何可以帮助我解决新问题的人!

The answer to my initial problem has already been answered in the "UPDATE" section of the question.我最初的问题的答案已经在问题的“更新”部分得到了回答。

If anyone has the same issue with the second part (references to document in bootstrap.js) then here is the solution I've got.如果有人对第二部分有同样的问题(对 bootstrap.js 中文档的引用),那么这就是我得到的解决方案。 I'm not saying this is good practice but it compiles and works as expected for my project (I now have tooltips!)我并不是说这是一个好的做法,但它可以按我的项目的预期编译和工作(我现在有工具提示!)

So in _app.js where I was originally importing bootstrap inside useEffect I have replaced that with the following:因此,在我最初在 useEffect 中导入引导程序的useEffect中,我已将其替换为以下内容:

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();
  }, []);

So from what I gather, because we're calling some parts of the bootstrap library directly, we need to make sure this is being done client-side only, and so it needs to happen in the useEffect hook.因此,据我所知,因为我们直接调用引导库的某些部分,我们需要确保这仅在客户端完成,因此需要在useEffect挂钩中进行。 To be able to target bootstrap.Tooltip we need to import bootstrap inside the useEffect hook and assign it to a variable.为了能够以bootstrap.Tooltip为目标,我们需要在useEffect挂钩中导入bootstrap并将其分配给一个变量。 This is all put in an async func that is declared and then called in the useEffect hook.这一切都放在一个异步函数中,该函数被声明然后在useEffect挂钩中调用。

If you need any other bootstrap js code that directly or indirectly refers to document (which most of it does), then that will have to go in here too.如果您需要任何其他直接或间接引用document的引导 js 代码(大部分都这样做),那么这里也必须使用 go。

And if you only need it on a specific page rather than _app.js then you can just move it there.如果您只在特定页面而不是 _app.js 上需要它,那么您可以将它移到那里。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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