renderer.render() method makes an WebGL error in useEffect

import React, { useEffect, useRef } from 'react';
import './App.css';
import * as THREE from 'three'

function App() {
  console.log('App');

  const threeCanvas = useRef<HTMLCanvasElement>(null);
  useEffect(() => {
    const scene = new THREE.Scene()
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
    camera.position.set(0, 0, 50)
    const canvas = threeCanvas.current
    if (!canvas) return
    const boxGeometry = new THREE.BoxGeometry(10, 10, 10)
    const boxMaterial = new THREE.MeshNormalMaterial()
    const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial)
    const renderer = new THREE.WebGLRenderer({ canvas })
    scene.add(boxMesh)
    const animate = () => {
      boxMesh.rotation.x += 0.01;
      boxMesh.rotation.y += 0.01;
      renderer.render(scene, camera)
      window.requestAnimationFrame(animate)
      // console.log('animate');

    }
    animate()
  })
  return (
    <div className="App">
      <canvas id="threeCanvas" ref={threeCanvas} />
    </div>
  );
}

export default App;

error in console.

three.module.js:17398 WebGL: INVALID_OPERATION: uniformMatrix4fv: location is not from current program
setValueM4 @ three.module.js:17398
setValue @ three.module.js:18026
setProgram @ three.module.js:28321
WebGLRenderer.renderBufferDirect @ three.module.js:27254
renderObject @ three.module.js:27862
renderObjects @ three.module.js:27831
renderScene @ three.module.js:27753
WebGLRenderer.render @ three.module.js:27573
animate @ App.tsx:23
requestAnimationFrame (async)
animate @ App.tsx:24
requestAnimationFrame (async)
animate @ App.tsx:24
(anonymous) @ App.tsx:28
commitHookEffectListMount @ react-dom.development.js:23150
commitPassiveMountOnFiber @ react-dom.development.js:24926
commitPassiveMountEffects_complete @ react-dom.development.js:24891
commitPassiveMountEffects_begin @ react-dom.development.js:24878
commitPassiveMountEffects @ react-dom.development.js:24866
flushPassiveEffectsImpl @ react-dom.development.js:27039
flushPassiveEffects @ react-dom.development.js:26984
(anonymous) @ react-dom.development.js:26769
workLoop @ scheduler.development.js:266
flushWork @ scheduler.development.js:239
performWorkUntilDeadline @ scheduler.development.js:533

This creates a new three-renderer and a new frameloop on every prop change, or when the component gets triggered, you’re pulling the rug. It makes little sense to use three in react like that because you’re mixing two conflicting worlds: oop and fp, loosing all interop. I would suggest you use react-three-fiber: Basic demo - CodeSandbox

1 Like