I’m not an expert with ReactJS. I am trying to figure out the best approach for integrating ReactJS with BabylonJS when using React.StrictMode.
I do not want to use react-babylonjs for declarative syntax (yet), but I suspect it would have same problem with React.StrictMode. Same for babylonjs-hook
. The React.StrictMode
is not addressed in the related Babylon.js docs.
The core problem is that React.StrictMode
calls useEffect (and useState) twice while in development. There is no obvious way (to me) to initialize babylon.js only once within a function component.
Below shows the code I’ve used to try and allow everything to be initialized twice (including the engine), but that has some issues and seems very inefficient, even if happens only in development.
Is there some other, better, way to do this? Perhaps I should initialize babylonjs in an external module outside of any reactjs component?
Fwiw, here’s code I used to allow double initialization. Very similar to the useEffect hook in the babylonjs docs, but with unmount handling. It works except for an issue/bug with skybox seemingly not being fully disposed and/or webgl state. But again, means reinitializing babylonjs twice on every mount while in development.
function App() {
const canvasRef = React.createRef<HTMLCanvasElement>()
useEffect(() => {
console.log('useEffect')
// Create engine and a scene
const babylonEngine = new Engine(canvasRef.current, true)
const scene = new Scene(babylonEngine)
// Create a camera
const camera = new FlyCamera('Camera', new Vector3(0, 0, -2), scene)
camera.attachControl(true)
// Create a default environment
const envHelper = new EnvironmentHelper(
{
createSkybox: true, // When true, there are WebGL errors on 2nd init. No errors if false.
skyboxSize: 30,
groundColor: new Color3(0.5, 0.5, 0.5)
},
scene
)
// Setup resize handler
const onResize = () => {
babylonEngine.resize(true)
}
if (window) {
window.addEventListener('resize', onResize)
}
babylonEngine.runRenderLoop(() => {
scene?.render()
})
return () => {
console.log('unmount')
if (window) {
window.removeEventListener('resize', onResize)
}
envHelper.dispose() // doesn't seem to make a difference
scene.dispose()
babylonEngine.dispose()
}
}, []) // empty array to run useEffect only once ...but doesn't work for React.StrictMode
// ... etc
Here is the React.StrictMode setup, which I believe is the default in many starter reactjs templates now:
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
)