Issue
So I have the following react component which allows me to append three.js canvas to a ref object of type HTMLDivElement:
import { useEffect, useRef } from "react";
import * as THREE from 'three';
export default function Viewport() {
const mountRef = useRef<HTMLDivElement>(null);
const { current } = mountRef;
useEffect(() => {
if (!current) { return; }
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, current.clientWidth / current.clientHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
const { domElement } = renderer;
renderer.setClearColor(0xfff5ff,1)
renderer.setSize(current.clientWidth, current.clientHeight);
current.appendChild(domElement);
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
var animate = function () {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
};
animate();
return () => {
current.removeChild(domElement);
};
}, []);
return (
<div ref={mountRef} style={{width: '100%', height: '100%'}}>
</div>
);
}
The component works, but not every time I mount it! this is the output "Most of the time":
If I just add:
console.log(current);
The code would work just fine, and the HTMLDivElement object will be printed out to the console!
What am I missing here?
Here is a CodeSandBox link to the project: https://codesandbox.io/s/unruffled-burnell-3if54?file=/src/Viewport.tsx
Solution
You're closing over a stale copy of the current property of mountRef. The initial value of current is null, which is what you get the first time you grab it on the next line. Then React renders your component and sets current to refer to the div, and then your useEffect callback is called — but it still has null.
Instead, use the updated property in the useEffect callback; see comments:
export default function Viewport() {
const mountRef = useRef<HTMLDivElement>(null);
// Don't grab `current` here
useEffect(() => {
// Grab it here
const { current } = mountRef;
if (!current) { return; }
// ...
Answered By - T.J. Crowder
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.