Next.js动态导入组件时如何访问子组件内部状态变量
核心问题
你当前的写法从根上就不成立:
- React 函数组件内用
useState创建的状态是和单个组件渲染实例绑定的,你在组件文件里直接导出taskImg,拿到的根本不是组件运行时实时更新的状态值,没有任何实际作用。 - 只要你静态引入
Mapcomp文件的任何导出项,Next.js 服务端渲染阶段就会解析整个文件的依赖链,react-leaflet本身强依赖浏览器端window对象,必然触发window is not defined报错。
实现方案
用父子组件通信最标准的状态提升+回调传值方案即可,全程不需要静态导入Mapcomp的任何内容,完全规避SSR报错:
1. 调整父页面(All组件)
把需要共享的taskImg状态直接定义在父组件中,将状态更新方法作为prop传给动态加载的Map组件即可,动态导入的配置完全不需要改:
import { useState } from "react"; import dynamic from 'next/dynamic' import Navbar from "../components/Navbar"; import Footer from "../components/Footer"; const Map = dynamic(() => import('../components/Mapcomp'), { ssr: false, }) const All = () => { // 共享状态提升到父组件持有 const [taskImg, setTaskImg] = useState(null); return ( <> <Navbar /> <div className="bg-white w-[500px] h-[100vh]"> <div className="task border-4 border-red-500 h-[250px]"> {/* 直接在这里使用taskImg即可 */} {taskImg && <img src={taskImg} alt="task" />} </div> <div className="flex justify-center gap-[200px] text-xl"> <span>Tasks:</span> <span>Points:</span> </div> <div className="flex justify-center gap-[240px] text-xl"> <span>X</span> <span>X</span> </div> </div> {/* 把更新状态的方法传给子组件 */} <Map onTaskImgChange={setTaskImg} /> <Footer /> </> ); } export default All;
2. 调整Mapcomp组件
接收父组件传入的回调方法,组件内部taskImg更新时,同步调用回调把值传给父组件即可,记得删掉原来错误的export {taskImg}代码:
import { MapContainer, ImageOverlay } from 'react-leaflet' import { useEffect, useState } from "react"; import Head from "next/head"; import { LatLngBounds } from 'leaflet'; const Map = ({ onTaskImgChange }) => { const [userPos, setUserPos] = useState(null) const [taskPos, setTaskPos] = useState(null) const [task, setTask] = useState() const [taskImg, setTaskImg] = useState(null); const bounds = new LatLngBounds([81.505, -0.09], [50.773941, -84.12544]) useEffect(() => { const getRandomTask = (arr) => arr[Math.floor(Math.random() * arr.length)]; const tasks = ["boller", "placeholder"]; setTask(getRandomTask(tasks)); },[]) useEffect(() => { let currentTaskImg = null; if(task === "boller"){ currentTaskImg = "/sf2.jpg" } setTaskImg(currentTaskImg); // 把最新值同步给父组件 onTaskImgChange?.(currentTaskImg); },[task, onTaskImgChange]) return ( <> <Head> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossOrigin=""/> <script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js" integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og==" crossOrigin=""></script> </Head> <MapContainer className='absolute h-[750px] w-[750px] left-[45%] top-[80px] bg-no-repeat bg-cover bg-[#738aaf]' center={[71.505, -40.09]} zoom={3} scrollWheelZoom={true} noWrap={true}> <ImageOverlay url="/allmaphres.png" bounds={bounds} opacity={1} zIndex={10} /> </MapContainer> </> ); } export default Map;
注意点
- 这个方案完全不会触发SSR报错:Mapcomp始终是
ssr:false动态加载,服务端不会执行组件内部代码,从根源上规避了leaflet依赖window对象的问题。 - 不要尝试用导出组件ref、模块级变量之类的歪路实现,父子组件通信用状态提升是React官方推荐的标准方案,逻辑最清晰也最不容易出bug。
- 如果后续这个
taskImg需要在更多跨层级组件里使用,再考虑引入Context或者状态管理库即可,当前场景不需要额外加依赖。
内容的提问来源于stack exchange,提问作者Jaydendev
相关产品推荐
相关产品推荐

