使用react-three-fiber扩展时,NextJS窗口未定义错误

w6mmgewl  于 2023-08-04  发布在  React
关注(0)|答案(1)|浏览(117)

当运行代码时,我得到一个未处理的运行时错误,窗口未定义。网站运行正常,但构建失败。
验证码:

"use client";

import React, {useRef, useLayoutEffect, useEffect} from 'react';
import {Canvas, useFrame, extend, useThree} from "@react-three/fiber";
import ThreeGlobe from "three-globe";
import countries from './globe-data-min.json';
import {Color, DirectionalLight, PointLight, NoToneMapping} from "three";
import Loading from "@/app/loading";

extend({ThreeGlobe})

const CameraPosition = () => {
    const { camera } = useThree();
    camera.position.z = 250;
    camera.fov = 50
    return null;
};

const GlobeObj = () => {

    const globeRef = useRef();

    // data for arcs
    const N = 50;

    const arcsData = [...Array(N).keys()].map(() => ({
        startLat: (Math.random() - 0.5) * 180,
        startLng: (Math.random() - 0.5) * 360,
        endLat: (Math.random() - 0.5) * 180,
        endLng: (Math.random() - 0.5) * 360,
        color: ['#7b2080', 'white', '#4fa1e1', '#c21bcb'][Math.round(Math.random() * 3)]
    }));

    useFrame( () => {
            if (globeRef.current) {
                globeRef.current.rotation.y += 0.003;
            }
        }
    );

    useLayoutEffect(()=> {
        const globeMaterial = globeRef.current.globeMaterial()
        globeMaterial.color = new Color(0x3a228a)
        globeMaterial.emissive = new Color(0x220038);
        globeMaterial.emissiveIntensity = 0.1;
        globeMaterial.shininess = 0.7;

        globeRef.current.showAtmosphere(true)
        globeRef.current.atmosphereColor("#3a228a")
        globeRef.current.atmosphereAltitude(0.25)

        globeRef.current.hexPolygonsData(countries.features)
        globeRef.current.hexPolygonResolution(3)
        globeRef.current.hexPolygonMargin(0.7)
        globeRef.current.hexPolygonColor((e) => {
            if (
                ["KGZ", "KOR", "THA", "RUS", "UZB", "IDN", "KAZ", "MYS"].includes(
                    e.properties.ISO_A3
                )
            ) {
                return "rgba(255,255,255, 1)";
            } else return "rgba(255,255,255, 0.7)";
        })

        globeRef.current.rotation.x = .5
        globeRef.current.rotation.z = .3

        globeRef.current.arcsData(arcsData)
        globeRef.current.arcColor('color')
        globeRef.current.arcDashLength(0.4)
        globeRef.current.arcDashGap(4)
        globeRef.current.arcDashInitialGap(() => Math.random() * 5)
        globeRef.current.arcDashAnimateTime(2000)

    })

    return (
        <threeGlobe
            ref={globeRef}
        />
    )
}

const Globe = () => {
    const meshRef = useRef();

    const canvasWidth = typeof window !== 'undefined' ? window.innerWidth : 800;
    const canvasHeight = typeof window !== 'undefined' ? window.innerHeight : 600;

    return (
        <Canvas
            gl={{ antialias: true, toneMapping: NoToneMapping }}
            size={{ width: canvasWidth, height: canvasHeight }}
            resize={{scroll: false}}
        >

            <ambientLight intensity={0.01} color={0xbbbbbb}/>
            <fog attach={'fog'} color={0x535ef3} near={1} far={2000}/>
            <directionalLight color={0xffffff} intensity={0.5} position={[-800, 1500, 300]}/>
            <directionalLight color={0x7982f6} intensity={0.8} position={[-300, 400, 50]}/>
            <spotLight color={'802959'} intensity={0.8} position={[50, 500, -40]} angle={0.5}/>
            <directionalLight color={'#bff7ff'} intensity={3} position={[-300, 400, -300]}/>
            <directionalLight color={'#bff7ff'} intensity={2} position={[-300, 400, -150]}/>
            <directionalLight color={'#bff7ff'} intensity={50} position={[-250, 250, -450]}/>
            <directionalLight color={'#bff7ff'} intensity={50} position={[-270, 0, -470]}/>
            <directionalLight color={'#bff7ff'} intensity={50} position={[0, 250, -500]}/>
            <pointLight color={0x8566cc} intensity={0.8} position={[-200, 500, 450]}/>
            <CameraPosition/>
            <GlobeObj/>

        </Canvas>
    );
};

export default Globe;

字符串
下面是调用堆栈:

Call Stack
updateDehydratedSuspenseComponent
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (16356:0)
updateSuspenseComponent
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (16056:0)
beginWork$1
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (17369:0)
beginWork
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (25672:0)
performUnitOfWork
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (24523:0)
workLoopSync
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (24239:0)
renderRootSync
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (24204:0)
performConcurrentWorkOnRoot
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (23343:0)
workLoop
node_modules/next/dist/compiled/scheduler/cjs/scheduler.development.js (261:0)
flushWork
node_modules/next/dist/compiled/scheduler/cjs/scheduler.development.js (230:0)
MessagePort.performWorkUntilDeadline
node_modules/next/dist/compiled/scheduler/cjs/scheduler.development.js (537:0)


我已经把问题缩小到

extend({ThreeGlobe})


这导致了窗口错误。我想知道是否有一种方法可以使用钩子来扩展而不会导致窗口错误。

  • 编辑:我更改了代码以在开始时使用useEffect,但我得到了无效的钩子调用。
"use client";

import React, {useRef, useLayoutEffect, useEffect} from 'react';
import {Canvas, useFrame, extend, useThree} from "@react-three/fiber";
import countries from './globe-data-min.json';
import {Color, DirectionalLight, PointLight, NoToneMapping} from "three";
import Loading from "@/app/loading";

useEffect(()=> {
    const ThreeGlobe = () => import("three-globe")
}, [])

extend({ThreeGlobe})
...


编辑2:以下是我的尝试:1

const Globe = () => {
    const meshRef = useRef();

    useEffect(() => {
        import("@react-three/fiber").then(({ extend }) => {
            extend({ ThreeGlobe });
        });
    }, []);

    return (
        // ... (rest of the component code)
    );
};


这导致

Unhandled Runtime Error
Error: R3F: ThreeGlobe is not part of the THREE namespace! Did you forget to extend?


2

const Globe = () => {
    const meshRef = useRef();

    useEffect(() => {
        extend({ ThreeGlobe });
    }, []);

    return typeof window !== 'undefined' ? (
        <Canvas
            gl={{ antialias: true, toneMapping: NoToneMapping }}
            size={{ width: window.innerWidth, height: window.innerHeight }}
            resize={{ scroll: false }}
        >
            <ambientLight intensity={0.01} color={0xbbbbbb} />
            {/* ... (other lights and components) */}
            <GlobeObj />
        </Canvas>
    ) : null;
};


这仍然会导致窗口未定义
3

// changing ThreeGlobe import
const ThreeGlobe = React.lazy(() => import('three-globe'));
... 
//rest of the code is the same


我要

Target is not a constructor

cdmah0mi

cdmah0mi1#

你会得到这个错误,因为Next.js在构建过程中在服务器端呈现你的组件。该时间窗口对象不可用。
要修复此错误,需要在useEffect内部导入依赖于浏览器API的库

useEffect(()=>{
   const myLib = () => import(`path-to-my-lib`)
   ...
}, [])

字符串

相关问题