How to change mesh properties with react state hook?

Hi my friends,

I am a beginner in react and babylonJS,
I am following the documents to learn about react and babylonJS , and tried to create a editor, I have put the babylonJS component in a parent component, that allows some interactions to pass the props to children, For example, to change the grid size and grid plane width, with user input, but I do not know how to change the babylonJS scene with the updated state, for example, I am trying to update the planesize and gridsize that I used to create the groundmesh. How can I achieve that?

Many thanks!

    import React from 'react';
    import { ArcRotateCamera, Color3,Vector3, HemisphericLight, MeshBuilder} from '@babylonjs/core';
    import {GridMaterial} from '@babylonjs/materials';
    import SceneComponent from './SceneComponent';
    import './ViewPortComponent.css';

    const ViewPortComponent = props => {
      const [settingData, setSettings] = React.useState( {
        planesize: 6000,
        gridsize: 50,
      } );

      if (props.children.planesize !== settingData.planesize){
        setSettings({
          planesize: props.children.planesize,
          gridsize: props.children.gridsize,
        })
      }
      

      React.useEffect( () => {
        })

     
    const onSceneReady = (scene) => {
        var camera = new ArcRotateCamera("mainCamera", 0, 0, 10, new Vector3(0, 0, 0), scene);
        camera.setPosition(new Vector3(0, 500, -600));
        const canvas = scene.getEngine().getRenderingCanvas();
        camera.attachControl(canvas, true);
        var light = new HemisphericLight("light", new Vector3(0, 500, 0), scene);
        light.intensity = 0.7;


        let ground = MeshBuilder.CreateGround("ground", {width: settingData.planesize, height: settingData.planesize}, scene);
        let groundMaterial = new GridMaterial("groundMaterial", scene);
        groundMaterial.majorUnitFrequency = 10; 
        groundMaterial.minorUnitVisibility = 0.5;
        groundMaterial.gridRatio = settingData.gridsize; 
        groundMaterial.backFaceCulling = false;
        groundMaterial.mainColor = new Color3(1, 1, 1);
        groundMaterial.lineColor = new Color3(1.0, 1.0, 1.0);
        groundMaterial.opacity = 0.98;
        ground.material = groundMaterial;
      }

      const onRender = scene => {
      }
      
      return(
        <SceneComponent antialias onSceneReady={onSceneReady} onRender={onRender} id='viewport'>
        </SceneComponent>
      )
    }

    export default ViewPortComponent;

Adding @brianzinn who is amazing in this space.

1 Like

You have to do updates in useEffect block. For example:

    const sceneRef = React.useRef(null);

    React.useEffect( () => {
      const scene = sceneRef.current;
      if(scene){
        // do anything with scene and updated settingData here...
      }
    }, [settingData]);

    const onSceneReady = (scene) => {
      sceneRef.current = scene;
      // ...
2 Likes

Thanks @sebavan, I appreciate your help!

Hi @avin, thanks, let me try your method. appreciate the help!

Thanks for the help, now I can pass my props and deconstruct it properly before the useEffect(), I can update the scene, create a ground each time, the scene will keeps adding new planes if I make change on parent props, how can I just edit the exsiting ground plane?
Thank you very much, and this is the first time I posted a question in babylon js forum, it is really awesome here!!! lol

I think, if you change points of the object - you have to dispose old mesh and create new one

Yea, avin, I think you are right, my approach is first to get the plane by ID, and compare with the state, and I found that for attributes like height and width are not changeable, but can be scaled, I just dispose that object and recreate a new one instead.
Thank you so much for the advice!!!

hi @daguozi - welcome to the forum!

The answer from avin is perfect. I just wanted to add why useState wouldn’t work to provide a deeper understanding - calling the state update function does trigger a re-render, but that would not re-run your code that uses those values.

The equivalent (untested) code using react-babylonjs would work with the useState hook, but it’s because the reconciler flows the property differences through. I’m using the ‘key’ as a way to automatically dispose the mesh when you have a different planesize.

<ground name='ground' key={`ground-${settingData.planesize`} width={settingData.planesize} height={settingData.planesize}>
  <gridMaterial name='groundMaterial' gridRatio={settingData.gridsize} majorUnitFrequency={10} ... />
</ground>

That’s a re-usable component then if you went that way as well. Cheers.

1 Like

Hi @brianzinn, yes I think I understand now, It is a great solution here you provided, and I appreciate your help! it really makes sense to me now. I know this react-babylonjs npm project and I think it is a great alternative, but as a beginner, I decided for now just to use the babylon/core to dive in and learn the APIs with deeper understanding lol. and thanks for explaining the automatic dispose!

1 Like