Next.js:window未定义。

14

我正在尝试在Next.js应用程序中使用apexcharts,但它返回“window未定义”错误。

我希望能得到任何关于此问题的帮助。

有人知道发生了什么以及为什么会这样吗?

import React from 'react';
import Chart from 'react-apexcharts';

export default class Graficos extends React.Component <{}, { options: any, series: any }> {
    constructor(props:any) {
        super(props);

        this.state = {
            options: {
            chart: {
                id: "basic-bar"
            },
            xaxis: {
                categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999]
            }
            },
            series: [
            {
                name: "series-1",
                data: [30, 40, 45, 50, 49, 60, 70, 91]
            }
            ]
        };
    }
  
    render() {
        return (
            <div className="row">
                <h1>Gráfico Básico</h1>
                <div className="mixed-chart">
                    <Chart
                        options={this.state.options}
                        series={this.state.series}
                        type="bar"
                        width={500}
                    />
                </div>
            </div>
        );
    }
  }

你能提供一个最小可复现示例吗?而不是一些代码片段。 - Yogesh Yadav
4个回答

34

Next.js的一个关键特点是它可以在服务器端或甚至在构建时呈现您的React应用程序的部分内容。虽然这有助于提高页面性能,但缺点是服务器不提供与浏览器中应用程序可以访问的所有相同的API。在这种情况下,没有定义全局的window对象。

不幸的是,在apexcharts.js的源代码中搜索会找到许多对window的引用:https://github.com/apexcharts/apexcharts.js/search?q=window。这也出现在他们的React包装器中:https://github.com/apexcharts/react-apexcharts/blob/ecf67949df058e15db2bf244e8aa30d78fc8ee47/src/react-apexcharts.jsx#L5。虽然似乎没有方法可以让apexcharts避免引用window,但您可以防止Next.js在服务器上使用图表。最简单的方法是将对代码的任何引用都包装在对是否定义了window的检查中,例如:

<div className="mixed-chart">
  {(typeof window !== 'undefined') &&
  <Chart
    options={this.state.options}
    series={this.state.series}
    type="bar"
    width={500}
  />
  }
</div>  

使用apexcharts时,由于单独的组件导入将触发对window的引用,因此您还需要为组件导入执行此操作,如第二个链接所示。为了解决这个问题,您需要使用动态导入而不是当前使用的普通导入:https://nextjs.org/docs/advanced-features/dynamic-import

import dynamic from 'next/dynamic'

const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });

7
在使用TypeScript进行动态导入时,我遇到了类型不匹配的问题,这在Next.js 13中无法解决。然而,我通过创建一个名为ApexChart的新组件来解决了这个问题。
在新的ApexChart组件中,我导入了react-apexcharts库并将所需的props传递给它。以下是该组件的示例:
"use client";
import { useEffect, useState } from "react";

export default function ApexChart(props: any) {
  const [Chart, setChart] = useState<any>();
  const hasType = typeof props?.type !== "undefined";

  useEffect(() => {
    import("react-apexcharts").then((mod) => {
      setChart(() => mod.default);
    });
  }, []);

  return hasType && Chart && <Chart {...props} />;
}

4
if (typeof window !== "undefined") {
    host = window.location.host;
    console.log("host--------------", host);
  }

0
为什么在next.js中没有定义documentwindow
function MyComponent() {
  // useMemo will run in server-side & client-side
  // Error: window is not defined in server
  const color = useMemo(()=> {
    return window.globalColor; 
  }, [])
  return <h1 className={`title ${color}`}>Hello World!</h1>
}

为了解决这个问题,你需要使用 useEffect 只在客户端运行渲染。 以下是一个示例代码片段:
function MyComponent() {
  const [color, setColor] = useState()
  useEffect(() => {
    setColor(window.globalColor)
  }, [])
  return <h1 className={`title ${color}`}>Hello World!</h1>
}

此外,将上述解决方案包装在一个钩子中。
function useClientMemo<T>(fn: () => T, deps?: DependencyList): T {
  const [data, setData] = React.useState<T>();
  React.useEffect(() => {
    setData(fn());
  }, deps);
  return data;
}
export default useClientMemo;

然后,只需使用:
function MyComponent() {
  const color = useClientMemo(()=> {
    return window.globalColor; 
  }, [])
  return <h1 className={`title ${color}`}>Hello World!</h1>
}

解释:

  • useEffect 只在客户端渲染时执行。
  • useMemo 在客户端渲染和服务器端渲染时都会执行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接