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.