useCesium() in Next.js always returns an empty object
Custom base viewer component + dynamic import:
src/components/Cesium/CesiumCore.tsx
import { Viewer } from "resium";
import { useProvider } from "../../hooks/useProvider";
export interface CesiumProps {
children?: React.ReactNode | JSX.Element;
style?: React.CSSProperties;
}
export default function CesiumCore({ children, style = {} }: CesiumProps) {
const providers = useProvider();
return (
<Viewer
requestRenderMode={true}
homeButton={false}
sceneModePicker={false}
navigationHelpButton={false}
baseLayerPicker={false}
geocoder={false}
fullscreenButton={false}
animation={false}
timeline={false}
imageryProvider={providers.bingProvider}
style={style}
>
{children || null}
</Viewer>
);
}
src/components/Cesium/index.tsx
import dynamic from "next/dynamic";
const Cesium = dynamic(() => import("./CesiumCore"), { ssr: false });
export default Cesium;
Actual page using dynamically imported component:
src/pages/index.tsx
import { useCesium } from "resium";
import { Button } from "@mantine/core";
import Cesium from "../components/Cesium";
export default function HomePage() {
const cesium = useCesium();
return (
<Cesium>
<Button
sx={{ position: "absolute", top: 0, left: 0 }}
onClick={() => {
// Never entering, `cesium` is always `{}`
console.warn(cesium);
if (!cesium || !cesium.viewer) return;
cesium.viewer.camera.flyHome();
}}
>
Click me
</Button>
</Cesium>
);
}
The object provided by Resium's context is always empty and I suspect this is because of SSR. Any workaround for this? Thanks!
I saw a similar issue with Next.js and SSR - in the end, I went back to a traditional (and original) useRef construct. That worked for me.
Thanks for the reply. I ended up doing the same thing: I created a context where I provide a classic ref + setRef couple and then export the shortcut hook:
src/components/Cesium/CesiumCore.tsx
import { useEffect, useRef } from "react";
import { Viewer } from "resium";
import { useCesium } from "@/context/CesiumContext";
import type { Viewer as CesiumViewer } from "cesium";
import type { CesiumComponentRef } from "resium";
export interface CesiumProps {
children?: React.ReactNode | JSX.Element;
style?: React.CSSProperties;
}
export default function CesiumCore({ children, style = {} }: CesiumProps) {
const ref = useRef<CesiumComponentRef<CesiumViewer>>(null);
const { setRef } = useCesium();
useEffect(() => setRef(ref), []);
return (
<Viewer ref={ref} style={style}>
{children || null}
</Viewer>
);
}
src/context/CesiumContext.tsx
import { createContext, useContext, useEffect, useState } from "react";
import type { Viewer } from "cesium";
import type { CesiumComponentRef } from "resium";
import type { GlobalProps } from "@/types/misc";
export interface CesiumContextProps {
cesium: Viewer;
setRef: React.Dispatch<
React.SetStateAction<React.MutableRefObject<CesiumComponentRef<Viewer>>>
>;
}
export interface CesiumProviderProps {
children: React.ReactNode | JSX.Element | React.ReactElement<GlobalProps>;
}
export const CesiumContext = createContext<CesiumContextProps>(null);
export const CesiumProvider = ({ children }: CesiumProviderProps) => {
const [ref, setRef] =
useState<React.MutableRefObject<CesiumComponentRef<Viewer>>>(null);
const [cesium, setCesium] = useState<Viewer>(null);
useEffect(
() => ref?.current?.cesiumElement && setCesium(ref.current.cesiumElement),
[ref]
);
return (
<CesiumContext.Provider value={{ cesium, setRef }}>
{children}
</CesiumContext.Provider>
);
};
export const useCesium = () => useContext(CesiumContext);
Maybe dirty, but works like a charm.
Resium works only in dynamic, so all components referering to Resium and Cesium should be rendered in dynamic, I think.
Thanks for the insight. Makes sense. For now I'll settle with my custom context, but it would be nice if Resium exposed the equivalent of setRef so that this extra step can be avoided -- just a suggestion. Maybe the issue won't even subsist when migrating to the app folder with the use of "use client".
I think Cesium cannot work in Node.js (server side) env, so it is hard to create a content of ref in server side. We can consider using getServerSideProps to pass parameters to initialize Cesium Viewer from server side to client side.