hooksUsespace

useSpace (useViewform) Hook

The useSpace hook provides access to the core boundary explore functionality and state within your React components. It allows you to interact with and control various aspects of the 3D tour viewer.

Accessing the Store

There are two main ways to access the store in your application:

1. Using SpaceProvider (formerly ViewformProvider)

You can wrap your parent component with the SpaceProvider to provide store access throughout your application:

import { SpaceProvider } from './store';
 
function App() {
  return (
    <SpaceProvider>
      <YourComponents />
    </SpaceProvider>
  );
}

This makes it really easy to access the useSpace hook in a parent component. A child Space component will automatically pick up the parent context.

2. Using the Space Component Structure

The preferred method is to use the Space component’s built-in structure, which automatically provides store context to its children through designated containers:

import { Space, Overlay, Scene } from '@your-library/components';
 
function App() {
  return (
    <Space tourId="example-tour">
      <Overlay>
        {/* UI Components with store access */}
        <Loading />
        <FloorplanModal />
        <TagModal />
        <Toolbar />
      </Overlay>
      <Scene>
        {/* Three.js/Fiber Components with store access */}
        <Tags />
        <Hotspots />
        <Measurements />
        {/* Add any other fiber content here that needs store access */}
      </Scene>
    </Space>
  );
}

Component Structure Overview

Space (provides store context)
├── Overlay (for UI components)
│   ├── Loading
│   ├── FloorplanModal
│   ├── TagModal
│   └── Toolbar
└── Scene (for 3D/Fiber components)
    ├── Tags
    ├── Hotspots
    └── Measurements

Accessing Store in Components

Components placed within either Overlay or Scene automatically have access to the store:

// Component in Overlay
function Toolbar() {
  const currentCamera = useSpace(state => state.currentCamera);
  return <div>{/* UI implementation */}</div>;
}
 
// Component in Scene
function Hotspots() {
  const hotspots = useSpace(state => state.hotspots);
  return <>{/* Three.js/Fiber implementation */}</>;
}

Best Practices

  1. Component Placement

    • Place UI components in <Overlay>
    • Place Three.js/Fiber components in <Scene>
    • Both containers provide full store access
  2. Store Access Pattern

    // In any child component of Overlay or Scene
    function YourComponent() {
      const { 
        someState, 
        someAction 
      } = useSpace(
        state => ({
          someState: state.someState,
          someAction: state.someAction
        }),
        shallow
      );
     
      return <>{/* Your implementation */}</>;
    }
  3. Context Usage

    • All components within Space have access to the store context
    • No need for additional context providers
    • Store is automatically initialized with the provided tourId

Example: Complete Implementation

import { Space, Overlay, Scene } from '@your-library/components';
import { useSpace } from './store';
 
function App() {
  return (
    <Space tourId="example-tour">
      <Overlay>
        <CustomUI />
      </Overlay>
      <Scene>
        <Custom3DContent />
      </Scene>
    </Space>
  );
}
 
// UI Component Example
function CustomUI() {
  const { loading, currentCamera } = useSpace(
    state => ({
      loading: state.loading,
      currentCamera: state.currentCamera
    }),
    shallow
  );
 
  return (
    <div>
      {loading ? 'Loading...' : `Current Camera: ${currentCamera?.id}`}
    </div>
  );
}
 
// Scene Component Example
function Custom3DContent() {
  const cameras = useSpace(state => state.cameras);
 
  return (
    <group>
      {cameras.map((camera) => (
          <Sphere 
            key={camera.id} 
            position={[camera.position.x, camera.position.y, camera.position.z]}
            args={[0.1, 16, 16]} />
      ))}
    </group>
  );
}

This structure ensures proper separation of concerns while maintaining full access to the store’s functionality throughout your application.

Store Slices

TourSlice

Manages the state and operations for virtual tours.

Interface

export interface TourSlice {
    id?: string;
    instanceId?: string;
    loading: boolean;
    tour?: IViewformTour;
    initializeTour: (tour: IViewformTour, sasInfo: ISasInfo) => void;
    updateTour: (tour: IViewformTour, sasInfo: ISasInfo) => void;
    refresh: () => void;
    destroy: () => void;
}
Property/MethodTypeDescription
idstringOptional unique identifier for the tour
instanceIdstringOptional instance identifier for the tour
loadingbooleanIndicates if the tour is currently loading
tourIViewformTourCurrent tour data
initializeTourfunctionInitializes a new tour with given tour data and SAS information
updateTourfunctionUpdates an existing tour with new data
refreshfunctionRefreshes the current tour state
destroyfunctionCleans up and removes the current tour

CameraSlice

Handles camera management and state for 3D viewing.

Interface

export interface CameraSlice {
    cameras: ICameraDetails[];
    currentResolution: Resolution;
    currentCamera?: ICameraDetails;
    currentCameraIndex?: number;
    cubeMapLoader?: CubeMapLoader;
    setCurrentCamera: (camera: ICameraDetails, index: number) => void;
    setCubeMapLoader: (cubeMapLoader: CubeMapLoader) => void;
}
Property/MethodTypeDescription
camerasICameraDetails[]Array of available cameras
currentResolutionResolutionCurrent resolution settings
currentCameraICameraDetailsCurrently active camera
currentCameraIndexnumberIndex of current camera in cameras array
cubeMapLoaderCubeMapLoaderLoader for cubemap textures
setCurrentCamerafunctionSets the active camera and its index
setCubeMapLoaderfunctionSets the cubemap loader instance

ModelSlice

Manages 3D model state and operations.

Interface

export interface ModelSlice {
    modelCache: any;
    currentModels: IModelDetailsRich[];
    modelRef: Object3D | undefined;
    isSyntheticModel: boolean;
    setModelRef: (ref: Object3D, synthetic: boolean) => void;
    getModelDownloadUrl: (model: ModelDetails) => string;
    downloadModel: (model: ModelDetails) => Promise<GLTF>;
    modelViewType?: ModelViewType;
    setModelViewType: (m: ModelViewType) => void;
}
Property/MethodTypeDescription
modelCacheanyCache storage for loaded models
currentModelsIModelDetailsRich[]Currently active models
modelRefObject3DReference to the current 3D model
isSyntheticModelbooleanIndicates if current model is synthetic
setModelReffunctionSets the model reference and synthetic flag
getModelDownloadUrlfunctionGenerates download URL for a model
downloadModelfunctionDownloads a model asynchronously
modelViewTypeModelViewTypeCurrent model view type
setModelViewTypefunctionSets the model view type

ControlsSlice

Manages user controls and camera navigation.

Interface

export interface ControlsSlice {
    timeline: Timeline;
    animating: boolean;
    jumpMode: JumpMode;
    canZoom: boolean;
    zoom: number;
    canvas?: HTMLCanvasElement;
    camera?: PerspectiveCamera;
    frame?: HTMLElement;
    controlsEnabled: boolean;
    canTilt: boolean;
    canRotate: boolean;
    currentRotation: Euler;
    allowJumpYawRotationCorrection?: boolean;
    allowJumpPitchRotationCorrection?: boolean;
    controls: any;
}
Property/MethodTypeDescription
timelineTimelineControls animation timeline
animatingbooleanCurrent animation state
jumpModeJumpModeCurrent jump navigation mode
canZoombooleanWhether zoom is enabled
zoomnumberCurrent zoom level
canvasHTMLCanvasElementReference to canvas element
cameraPerspectiveCameraReference to camera
controlsEnabledbooleanWhether controls are enabled
canTiltbooleanWhether tilt is enabled
canRotatebooleanWhether rotation is enabled
currentRotationEulerCurrent camera rotation
controlsanyReference to controls instance

Plus numerous navigation methods:

  • setJumpMode
  • setAnimating
  • enableControls
  • disableControls
  • goToTag
  • goToCurrent
  • goToVersion
  • goToCamera
  • goToGroup
  • goToNearestCameraWithinFov
  • goToNearestCameraWithDirection
  • goBack

AnnotationSlice

Manages annotations and tags in the 3D space.

Interface

export interface AnnotationSlice {
    tags: IAnnotationDetails[];
    currentTag?: IAnnotationDetails;
    setCurrentTag: (tag?: IAnnotationDetails) => void;
}
Property/MethodTypeDescription
tagsIAnnotationDetails[]Array of annotation tags
currentTagIAnnotationDetailsCurrently selected tag
setCurrentTagfunctionSets the current active tag

ImageVersionSlice

Manages different versions of images.

Interface

export interface ImageVersionSlice {
    imageVersion?: IImageVersion;
    imageVersionIndex: number;
    getSelectableImageVersions: () => IImageVersion[];
    setImageVersion: (index: number) => void;
    setImageVersionByName: (name: string) => void;
    setImageVersionById: (id: string) => void;
}
Property/MethodTypeDescription
imageVersionIImageVersionCurrent image version
imageVersionIndexnumberIndex of current version
getSelectableImageVersionsfunctionReturns available image versions
setImageVersionfunctionSets version by index
setImageVersionByNamefunctionSets version by name
setImageVersionByIdfunctionSets version by ID

MeasurementSlice

Manages measurement functionality in 3D space.

Interface

export interface MeasurementSlice {
    canMeasure: boolean;
    points: Vector3[];
    measurements: Measurement[];
    addMeasurement: (point: Vector3) => void;
    clearMeasurements: () => void;
    enableMeasurement: (enable: boolean) => void;
    addMeasurements: (data: Measurement[]) => void;
}
Property/MethodTypeDescription
canMeasurebooleanWhether measurement mode is enabled
pointsVector3[]Array of measurement points
measurementsMeasurement[]Array of completed measurements
addMeasurementfunctionAdds a new measurement point
clearMeasurementsfunctionClears all measurements
enableMeasurementfunctionToggles measurement mode
addMeasurementsfunctionAdds multiple measurements at once

HotspotSlice

Manages interactive hotspots in the 3D environment.

Interface

export interface HotspotSlice {
    hotspots: IHotspotDetails[];
    currentHotspot?: IHotspotDetails;
    initializeHotspots: (tour: IViewformTour, shape: HotspotShape, animation: HotspotAnimation, color: string) => void;
    setCurrentHotspot: (camera: ICameraDetails) => void;
}
Property/MethodTypeDescription
hotspotsIHotspotDetails[]Array of available hotspots
currentHotspotIHotspotDetailsCurrently active hotspot
initializeHotspotsfunctionSets up hotspots with specified parameters
setCurrentHotspotfunctionSets the active hotspot

EventSlice

Manages event handling and callbacks.

Interface

export interface EventSlice {
    listeners: Listeners;
    addListeners: (listeners: Partial<Listeners>) => void;
    emitRotation: (e: Euler) => void;
    emitRotationStart: (e: Euler) => void;
    emitRotationEnd: (e: Euler) => void;
    emitJumpStart: (current: ICameraDetails, next: ICameraDetails) => void;
    emitJumpEnd: (current: ICameraDetails) => void;
    emitKeyframeStart: (current: Keyframe) => void;
    emitFov: (v: number) => void;
}
Property/MethodTypeDescription
listenersListenersCollection of event listeners
addListenersfunctionRegisters new event listeners
emitRotationfunctionTriggers rotation event
emitRotationStartfunctionTriggers rotation start event
emitRotationEndfunctionTriggers rotation end event
emitJumpStartfunctionTriggers camera jump start event
emitJumpEndfunctionTriggers camera jump end event
emitKeyframeStartfunctionTriggers keyframe start event
emitFovfunctionTriggers FOV change event

ResolutionSlice

Manages resolution settings and quality control.

Interface

export interface ResolutionSlice {
    loadDurations: ResolutionLoadDuration[];
    canvasPerformance: number;
    networkQuality: number;
    isMobileDevice: boolean;
    resolution: Resolution;
    minResolution: Resolution;
    selectedResolution?: Resolution;
    currentLoadedResolution?: Resolution;
    setCanvasPerformance: (value: number) => void;
    setIsMobileDevice: (value: boolean) => void;
    setNetworkQuality: (value: number) => void;
    computeResolution: () => void;
    recordCameraLoad: (duration: number, resolution: Resolution) => void;
    setSelectedResolution: (r?: Resolution) => void;
}
Property/MethodTypeDescription
loadDurationsResolutionLoadDuration[]History of load times
canvasPerformancenumberCanvas performance metric
networkQualitynumberNetwork quality metric
isMobileDevicebooleanDevice type indicator
resolutionResolutionCurrent resolution
minResolutionResolutionMinimum allowed resolution
selectedResolutionResolutionUser-selected resolution
setCanvasPerformancefunctionUpdates canvas performance value
computeResolutionfunctionCalculates optimal resolution
recordCameraLoadfunctionRecords camera load performance

LoadingSlice

Manages loading states.

Interface

export interface LoadingSlice {
    firstCameraLoaded: boolean;
    modelLoaded: boolean;
    modelLoading: boolean;
    setFirstCameraLoaded: (v: boolean) => void;
    setModelLoaded: (v: boolean) => void;
    setModelLoading: (v: boolean) => void;
}
Property/MethodTypeDescription
firstCameraLoadedbooleanInitial camera load state
modelLoadedbooleanModel load completion state
modelLoadingbooleanCurrent model loading state
setFirstCameraLoadedfunctionUpdates first camera load state
setModelLoadedfunctionUpdates model load state
setModelLoadingfunctionUpdates model loading state

CursorSlice

Manages cursor position and interaction.

Interface

export interface CursorSlice {
    cursorPosition: Vector3;
    currsorLookAt: Vector3;
    closestCamera?: CameraDetails;
    setCursor: (position: Vector3, lookAt: Vector3) => void;
}
Property/MethodTypeDescription
cursorPositionVector3Current cursor position
currsorLookAtVector3Cursor look-at point
closestCameraCameraDetailsNearest camera to cursor
setCursorfunctionUpdates cursor position and look-at

AttachmentSlice

Manages file attachments.

Interface

export interface AttachmentSlice {
    getAttachmentDownloadUrl: (attachment: AttachmentDetails) => string;
}
Property/MethodTypeDescription
getAttachmentDownloadUrlfunctionGenerates download URL for attachments

FlagSlice

Manages UI flags and states.

Interface

export interface FlagSlice extends FlagSliceProps {
    setUIFlag: (p: Partial<FlagSliceProps>) => void;
}
Property/MethodTypeDescription
openTagModalbooleanTag modal visibility state
openFloorplanModalbooleanFloorplan modal visibility state
selectingResolutionbooleanResolution selection state
debugbooleanDebug mode state
setUIFlagfunctionUpdates UI flag states

GroupSlice

Manages groups of elements.

Interface

export interface GroupSlice {
    groups: IGroupDetails[];
    currentGroups?: GroupDetails[];
    setCurrentGroups: (groups?: GroupDetails[]) => void;
}
Property/MethodTypeDescription
groupsIGroupDetails[]Available groups
currentGroupsGroupDetails[]Currently selected groups
setCurrentGroupsfunctionUpdates current group selection

ConfiguratorSlice

Manages configuration settings and themes.

Interface

export interface ConfiguratorSlice {
    configurators: ConfiguratorDetails[];
    currentConfigurator?: ConfiguratorDetails;
    currentThemes: ThemeDetails[];
    initilizeConfigurators: (configurators: ConfiguratorDetails[], themes: ThemeDetails[]) => void;
    setCurrentTheme: (theme: ThemeDetails) => string[];
    setCurrentThemesByVersion: (version: ImageVersion) => void;
    setCurrentConfigurator: (c?: ConfiguratorDetails) => void;
}
Property/MethodTypeDescription
configuratorsConfiguratorDetails[]Available configurators
currentConfiguratorConfiguratorDetailsActive configurator
currentThemesThemeDetails[]Current theme settings
initilizeConfiguratorsfunctionSets up configurators and themes
setCurrentThemefunctionUpdates current theme
setCurrentThemesByVersionfunctionSets themes based on version
setCurrentConfiguratorfunctionUpdates current configurator

Performance Optimization

Using Shallow Equality

Zustand’s shallow comparison helps prevent unnecessary re-renders when working with objects or arrays. Here’s how to use it effectively:

// BAD: Will re-render on every store update
const { camera, model } = useSpace(state => ({
  camera: state.currentCamera,
  model: state.currentModels
}));
 
// GOOD: Only re-renders when these specific values change
const { camera, model } = useSpace(
  state => ({
    camera: state.currentCamera,
    model: state.currentModels
  }),
  shallow
);

Selective State Updates

Only select the specific state you need:

// BAD: Subscribes to entire camera object
const camera = useSpace(state => state.currentCamera);
 
// GOOD: Subscribe only to needed properties
const cameraId = useSpace(state => state.currentCamera?.id);
const cameraPosition = useSpace(state => state.currentCamera?.position);

Memoizing Selectors

For complex selections, memoize your selectors:

// BAD: Creates new selector on every render
function Component() {
  const activeCameras = useSpace(state => 
    state.cameras.filter(camera => camera.active)
  );
}
 
// GOOD: Memoized selector
const selectActiveCameras = (state) => 
  state.cameras.filter(camera => camera.active);
 
function Component() {
  const activeCameras = useSpace(selectActiveCameras);
}

Atomic State Updates

Keep your state updates focused and atomic:

// BAD: Updating multiple slices at once
const updateEverything = useSpace(state => ({
  setCamera: state.setCurrentCamera,
  setModel: state.setModelRef,
  setControls: state.setControls
}));
 
// GOOD: Separate concerns
const setCamera = useSpace(state => state.setCurrentCamera);
const setModel = useSpace(state => state.setModelRef);
const setControls = useSpace(state => state.setControls);