javascript - 在回调中调用 useRef
问题描述
所以我从钩子之前就没有反应,以前没有使用过threeJS,但我试图用一颗石头击中2只鸟,所以如果这是一个菜鸟的错误,请原谅。
我要做的是在 react 文档正文中渲染 Three.js 场景,我试图通过在其中运行 three.js 代码useEffect()
并使用 useRef() 设置对我的 react 文档的引用来做到这一点,但是,显然 useEffect 在文档呈现之前运行,因此会中断,所以我尝试使用这样的 ref 回调
import { useRef, useEffect, useCallback } from "react";
// Packages
import * as THREE from "three";
// Styling
import "./homePage.scss";
function HomePage() {
// Declare a new mounting reference
const mountRef = useCallback((node) => {
if (node !== null) {
useRef(null);
}
}, []);
// Lifecycle hook
useEffect(() => {
console.log(mountRef);
// === THREE.JS CODE START ===
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// use ref as a mount point of the Three.js scene instead of the document.body
mountRef.appendChild(renderer.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();
// === THREE.JS CODE END ===
}, []);
return <div ref={mountRef} />;
}
export default HomePage;
但是,现在我有以下错误
React Hook "useRef" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook
我该如何解决这种情况?谢谢!
解决方案
您useRef
在组件的顶层使用,而不是在回调中使用(不要使用useCallback
它):
const mountRef = useRef(null);
然后在您的useEffect
, 中声明mountRef.current
为依赖项,并且仅在存在时才使用它,请参阅***
注释:
useEffect(() => {
// *** If we don't have the DOM element yet, wait for it
if (!mountRef.current) {
return;
}
// === THREE.JS CODE START ===
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// use ref as a mount point of the Three.js scene instead of the document.body
mountRef.current.appendChild(renderer.domElement);
// ^^^^^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− *** Use the DOM element
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();
// === THREE.JS CODE END ===
}, []);
您可能希望包含一个清理回调(您从中返回的函数)以从on 卸载useEffect
中删除三个 DOM 元素:div
useEffect(() => {
// ***
const { current } = mountRef;
if (!current) {
return;
}
// === THREE.JS CODE START ===
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// use ref as a mount point of the Three.js scene instead of the document.body
// ***
const {domElement} = renderer;
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();
// === THREE.JS CODE END ===
// ***
return () => {
current.removeChild(domElement);
};
}, []);
请注意我是如何抓取本地常量的mountRef.current
。renderer.domElement
这使得清理回调更加可靠,因为这些属性可以在useEffect
回调上下文之外更改。
现场示例:
const {useRef, useEffect} = React;
function HomePage() {
// Declare a new mounting reference
const mountRef = useRef(null);
// Lifecycle hook
useEffect(() => {
const { current } = mountRef;
console.log("current", current);
// If we don't have the DOM element yet, wait for it
if (!current) {
return;
}
// === THREE.JS CODE START ===
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// use ref's DOM ELEMENT as a mount point of the Three.js scene instead of the document.body
const { domElement } = renderer;
current.appendChild(renderer.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();
// === THREE.JS CODE END ===
// Cleanup callback on component unmount
return () => {
current.removeChild(domElement);
};
}, []);
return <div ref={mountRef} />;
}
ReactDOM.render(
<HomePage/>,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r124/three.min.js"></script>
推荐阅读
- python - 如何将 webhook 事件添加到烧瓶服务器?
- c# - ASP.net MVC 5 项目 SMTP 功能错误
- python-3.x - Lambda 过滤器函数不适用于两个变量
- c - 在 C 中使用整数进行布尔测试
- django - 发布到 Django 表单的时间比使用 Service Worker 后台同步生成的时间晚得多:CSRF 问题
- java - 有没有办法加载用户生成的 fxml?
- swift - 在 SwiftUI 中使用 TabView 时,NavigationView 无法正确显示
- python - RandomForest regression with pandas and sklearn: Input contains NaN, infinity or a value too large for dtype('float32')
- python - 使用 Qt 样式表的类实例的样式
- android - 使用 Kotlin 发送带有改造的 JSONObject