javascript - Next.js: fönster är inte definierat
Jag försöker använda apexcharts för en next.js-applikation och det returnerar 'window är inte definierad'.
Jag skulle älska all hjälp med det.
Vet någon vad som händer och varför?
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>
);
}
}
Svar #1
En av Next.js:s nyckelfunktioner är att det kan rendera delar av din React-applikation på servern eller till och med vid byggtid. Även om detta kan vara till hjälp för att förbättra prestandan på din sida, är nackdelen att servern inte tillhandahåller samma API:er som din applikation skulle ha tillgång till i webbläsaren. I detta fall finns det inget globalt window-objekt definierat.
Tyvärr ger en sökning i källkoden för apexcharts.js många referenser till window: https://github.com/apexcharts/apexcharts.js/search?q=window. Detta förekommer också i deras React-wrapper: https://github.com/apexcharts/react-apexcharts/blob/ecf67949df058e15db2bf244e8aa30d78fc8ee47/src/react-apexcharts.jsx#L5. Även om det inte verkar finnas något sätt att få apexcharts att undvika referenser till window, kan du förhindra att Next.js använder diagrammet på servern. Det enklaste sättet att göra detta är att omsluta alla referenser till koden med en kontroll om window är definierad, t.ex.
<div className="mixed-chart">
{(typeof window !== 'undefined') &&
<Chart
options={this.state.options}
series={this.state.series}
type="bar"
width={500}
/>
}
</div>
Med apexcharts behöver du också göra detta för komponentimporten eftersom själva importen kommer att utlösa en referens till window som visas i den andra länken. För att kringgå det problemet behöver du använda en dynamisk import istället för den vanliga importen du har just nu: https://nextjs.org/docs/advanced-features/dynamic-import
import dynamic from 'next/dynamic'
const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
Svar #2
Jag stötte på ett problem med typmatchning när jag arbetade med dynamiska importer med TypeScript fungerade inte för mig i Next.js 13. Däremot löste jag det genom att skapa en ny komponent som heter ApexChart.
I den nya ApexChart-komponenten importerade jag react-apexcharts-biblioteket och skickade de nödvändiga props till det. Här är ett exempel på hur komponenten ser ut:
"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} />;
}
