Appearance
useMapstedMap() — React custom hook
This recipe provides a reusable useMapstedMap() hook that encapsulates the init / destroy lifecycle and exposes the MapInstance handle to your components.
For the full tutorial covering Next.js App Router, StrictMode notes, and the 'use client' directive, see Embed with React and Next.js.
The hook
tsx
// src/hooks/useMapstedMap.ts
import { useEffect, useRef, useState } from 'react';
import { init } from '@mapsted/maps-js-api';
import type { MapInstance } from '@mapsted/maps-js-api';
interface UseMapstedMapOptions {
propertyId: number;
mapsDomain?: string;
onReady?: () => void;
}
interface UseMapstedMapResult {
containerRef: React.RefObject<HTMLDivElement | null>;
instance: MapInstance | null;
ready: boolean;
}
export function useMapstedMap({
propertyId,
mapsDomain = 'https://maps.mapsted.com',
onReady,
}: UseMapstedMapOptions): UseMapstedMapResult {
const containerRef = useRef<HTMLDivElement>(null);
const instanceRef = useRef<MapInstance | null>(null);
const [ready, setReady] = useState(false);
useEffect(() => {
if (!containerRef.current) return;
let cancelled = false;
init({
element: containerRef.current,
propertyId,
mapsDomain,
onload: () => {
if (!cancelled) {
setReady(true);
onReady?.();
}
},
}).then((instance) => {
if (cancelled) {
instance.destroy();
return;
}
instanceRef.current = instance;
});
return () => {
cancelled = true;
setReady(false);
instanceRef.current?.destroy();
instanceRef.current = null;
};
// onReady intentionally excluded — consumers should stabilize the
// reference with useCallback if they need it in deps.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [propertyId, mapsDomain]);
return {
containerRef,
instance: instanceRef.current,
ready,
};
}Usage
tsx
// src/components/MapView.tsx
import { useMapstedMap } from '../hooks/useMapstedMap';
export function MapView() {
const { containerRef, instance, ready } = useMapstedMap({
propertyId: 1643,
onReady: () => console.log('Map is ready'),
});
function handleNavigate() {
if (!ready || !instance) return;
// Use any Maps JS API method — import from '@mapsted/maps-js-api'
import('@mapsted/maps-js-api').then(({ navigateToFloorById }) => {
navigateToFloorById(42);
});
}
return (
<>
<div ref={containerRef} style={{ width: '100%', height: '100vh' }} />
{ready && (
<button onClick={handleNavigate}>Go to floor</button>
)}
</>
);
}Notes
- StrictMode safety: The
cancelledflag prevents theonloadcallback andinstanceRefassignment from running after the cleanup has already fired, which is the pattern React 18 StrictMode exercises in development. - Instance stability:
instanceRef.currentis a mutable ref, not state. The component does not re-render wheninstancechanges. If you need re-renders on instance availability, replaceuseRefwithuseStatefor the instance. onReadystability: Pass a stable function reference (e.g. wrapped inuseCallback) if you includeonReadyas a dependency elsewhere.