I keep seeing, "Failed to execute 'requestSession' on 'XRSystem': There is already an active, immersive XRSession" , when I try to use WebXR in React

Viewed 348

Has anyone successfully been able to use WebXR in React? I'm trying to convert the code in the following tutorial from vanilla.js to react.js :

https://www.devbridge.com/articles/ar-app-development-tutorial/

But I keep seeing an error saying:

Uncaught (in promise) DOMException: Failed to execute 'requestSession' on 'XRSystem': There is already an active, immersive XRSession.

I have no issues using the code in vanilla.js.

My React version of the code is:

import React, {useState} from 'react'

const AR = () => {

  const [xrButton, setXRButton] = useState('XR Not Found')
  const [info, setInfo] = useState('')

  //check if XR is supported or not
  const [supported, setSupported] = useState(false)

  const [check, setCheck] = useState(false)

  const { XRWebGLLayer } = window


  // to control the xr session
  let xrSession = null 

  // reference space used within an application
  let xrRefSpace = null 

  // Canvas OpenGL context used for rendering
  let gl = null 

  //Check if there are any WebXR errors in Window
  const checkXR = () => {

    if(!check){
      if (!window.isSecureContext) {
        document.getElementById("warning").innerText = "WebXR unavailable. Please use secure context" 
      }
      if (navigator.xr) { // check to see if WebXR is supported
        navigator.xr.addEventListener('devicechange', checkSupportedState) 
        checkSupportedState() 
      } else {
        document.getElementById("warning").innerText = "WebXR unavailable for this browser" 
      }
      setCheck(true)
    }
    
  }

  //check if webxr is supported
  const checkSupportedState = () => {
   
    if(navigator.xr){
      navigator.xr.isSessionSupported('immersive-ar').then((isSupported) => {
        setSupported(isSupported)
        if (isSupported) {
          setXRButton('View in AR')
          console.log("supported:", isSupported)
        } else {
          setXRButton('AR not found')
          console.log("WebXR doesn't support immersive-ar mode!") 
        }
        
      }) 
    }
    else {
      console.log("WebXR is not available!")
    }
  }

  //if webxr is supported, allow onButtonClicked handler to be called
  const onButtonClicked = () => {
    if (!xrSession) {
        navigator.xr.requestSession('immersive-ar', {
            optionalFeatures: ['dom-overlay'],
            requiredFeatures: ['local'],
            domOverlay: {root: document.getElementById('overlay')}
        }).then((session) => {
          xrSession = session
          setXRButton('Exit AR')
          onSessionStarted(xrSession)
        })
    } else {
      xrSession.end().then(() => xrSession = null)
    }
  }

  //when immersive-ar session starts
  const onSessionStarted = (session) =>{

    // Show which type of DOM Overlay got enabled (if any)
    if (session.domOverlayState) {
     setInfo('DOM Overlay type: ' + session.domOverlayState.type)
    }

    session.addEventListener('end', onSessionEnded);

    // create a canvas element and WebGL context for rendering
    let canvas = document.createElement('canvas');
    gl = canvas.getContext('webgl', { xrCompatible: true });
    session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });

    session.requestReferenceSpace('local').then((refSpace) => {
      xrRefSpace = refSpace;
      // start WebXR rendering loop
      session.requestAnimationFrame(onXRFrame);
    });

  }

  //once immersive-ar session ends
  const onSessionEnded = (event) => {
      xrSession = null;
      setXRButton( 'Enter AR')
      setInfo('')
      gl = null;
  }

  //on the frame once session starts
  const onXRFrame = (t, frame) => {
    let session = frame.session;
    session.requestAnimationFrame(onXRFrame);
    let pose = frame.getViewerPose(xrRefSpace);
    if (!pose) {
      return;
    }
    const pos = pose.transform.position;
    setInfo(`x:${pos.x.toFixed(2)}m y:${pos.y.toFixed(2)}m z:${pos.z.toFixed(2)}m`)
  }

  //call checkXR function
  checkXR()





  return (
    <div id="overlay">
      <div className="info-area">
        <div id="info">{info}</div>
        { supported ?
          <button id="xr-button" onClick={onButtonClicked}>{xrButton}</button>
            :
              <button id="xr-button" disabled>{xrButton}</button>

        }
        
      </div>
    </div>
  ) 
}

export default AR 
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

I'm not sure what I'm doing wrong. I haven't yet implemented any of the Three.js elements on the screen yet. I'm trying to test out if the immersive-ar session will start or not but I'm facing issues with it. The same steps followed in vanilla.js faced no issues.

0 Answers
Related