Removed meshes keep coming back in React/THREE.js app

Hi, I’m building a React web app with THREEjs driving a ‘Viewport’ component that contains the current 3D scene. The user might do something with the geometry, or else abandon that page (and the Viewport) to come back later with a fresh scene. There is no case where I want the scene data to live on after the viewport unmounts, so ideally on Viewport.componentWillUnmount() the scene would be purged entirely.

The scene is held as an element of Viewport’s state ( = this.state.scene in the code). I actually half expected this issue would take care of itself, since the state.scene resets itself to a new THREE.Scene each time the component mounts.

The core creation of the face geometry occurs within a method, like this:

      // for each face in the face list:
      for (let i = 0; i < fl.length; i++) {
        let F = fl[i];
        let geometry = new THREE.BufferGeometry();

        let pt0 = new THREE.Vector3(F.pt0[0], F.pt0[1], F.pt0[2]);
        let pt1 = new THREE.Vector3(F.pt1[0], F.pt1[1], F.pt1[2]);
        let pt2 = new THREE.Vector3(F.pt2[0], F.pt2[1], F.pt2[2]);
        let pt3 = new THREE.Vector3(F.pt3[0], F.pt3[1], F.pt3[2]);

        if (pt2.x !== pt3.x || pt2.y !== pt3.y || pt2.z !== pt3.z) {
          const vertices = new Float32Array([
            // 1st triangle
            pt0.x, pt0.y, pt0.z,
            pt1.x, pt1.y, pt1.z,
            pt3.x, pt3.y, pt3.z,

            // 2nd triangle
            pt1.x, pt1.y, pt1.z,
            pt2.x, pt2.y, pt2.z,
            pt3.x, pt3.y, pt3.z
          ]);
    
          geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
        } else {
          // 3-vert triangle
          const vertices = new Float32Array([
            pt0.x, pt0.y, pt0.z,
            pt1.x, pt1.y, pt1.z,
            pt2.x, pt2.y, pt2.z
          ]);
          geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
        }

        const mesh = new THREE.Mesh(geometry, material);
        this.state.scene.add(mesh);
        let wf_mesh = new THREE.Mesh(geometry, wireframe_material);
        this.state.scene.add(wf_mesh);
      }
    } // i loop

I have tried to remove and dispose of this geometry in several ways. My most recent attempt was to try to eliminate it during React’s componentWillUnmount. (I threw in the dispose line out of desperation)…

componentWillUnmount() {
    console.log("---> 1 Viewport.componentWillUnmount scene:", this.state.scene)
    // empty the scene of geometry
    while (this.state.scene.children.length > 0) {
      this.state.scene.remove(this.state.scene.children[0]);
      this.state.scene.dispose(this.state.scene.children[0]);
    }

    console.log("---> 2 Viewport.componentWillUnmount scene:", this.state.scene)

    this.state.scene = null;  //also tried " = new THREE.Scene()" here

    console.log("---> 3 Viewport.componentWillUnmount scene:", this.state.scene)

  }

When I look at the console log, things look correct: the children have been reduced from 5852 elements to zero, and then the state.scene shows itself null. (When I tried the " = new THREE.Scene()" statement instead of the null statement above, there was an empty scene with a new uuid, just as I expected.)

BUT… when I come back to the web page containing Viewport later with another fresh data set, I can tell from the console and from looking in the Viewport that the faces from the previous instance of Viewport are still there. See how the Array value below continues to keep going up.

I’m still a little new to THREEjs and I’ve never taken the time to zero out scene geometry before- what am I missing? thanks in advance for any guidance.

–Noah

This line has no effect. Scene.dispose() is deprecated. The method also never had parameters.

Apart from that little detail, it’s hard to provide more feedback. Consider to demonstrate the issue with a simplified live example.

I think you might need to read up on state management/life cycle in the react docs. You should never change component state like that, but use setState (or the useState() hook returned setter).

I suspect you might be much better off trying to work with react-three-fiber which takes away a lot of the pain of integrating threejs rendering into React apps.

Hope that helps!

1 Like