React+Babylon.js中useState变量在useEffect事件更新后未变化
问题:React + Babylon.js 中无法更新 cameraPosition 状态变量
我正在使用TypeScript、React和Babylon.js尝试更新状态变量cameraPosition,但状态似乎未被更新,监听状态变化的useEffect也没有输出。相关代码如下:
主组件代码:
const camera = scene?.cameras[0]; const prevPositionRef = useRef<Nullable<Vector3>>(null); const [cameraPosition, setCameraPosition] = useState<Vector3>( new Vector3(0, 0, 0) ); useEffect(() => { if (camera) { const onAfterInput = () => { if ( !prevPositionRef.current || !prevPositionRef.current.equals(camera.position) ) { console.log("change") // 相机位置变化时正常打印 setCameraPosition(camera.position); // 无法更新状态 prevPositionRef.current = camera.position.clone(); } }; camera.onAfterCheckInputsObservable.add(() => { onAfterInput(); }); return () => { camera.onAfterCheckInputsObservable.removeCallback(() => { onAfterInput(); }); }; } }, [camera]); console.log(cameraPosition) // 不打印更新后的位置
监听状态变化的代码:
useEffect(() => { console.log("cameraPosition updated:", cameraPosition); }, [cameraPosition]);
第一个console.log能在相机位置变化时正常打印,但第二个监听状态的console.log无输出,我希望能在其他组件中使用cameraPosition,请问问题出在哪里?
问题原因
引用类型的浅比较机制:
Babylon.js的Vector3是引用类型,camera.position始终指向同一个对象实例。React的useState更新时会进行浅比较,当你传递camera.position给setCameraPosition时,状态的引用没有发生变化,React会认为状态未更新,因此不会触发重渲染,也不会执行依赖cameraPosition的useEffect。清理函数的错误:
在useEffect的清理函数中,你创建了一个新的匿名函数() => { onAfterInput(); }来移除回调,但这个函数和之前添加的回调不是同一个引用,导致回调无法被正确移除,可能引发内存泄漏,但这不是状态不更新的直接原因。
解决方案
1. 传递克隆后的Vector3对象
调用setCameraPosition时,传入camera.position.clone(),这样每次都会创建一个新的Vector3实例,React能检测到引用变化,从而触发状态更新和重渲染:
setCameraPosition(camera.position.clone());
2. 修复清理函数
保存添加的回调函数引用,移除时直接使用这个引用,确保回调能被正确清理:
useEffect(() => { if (camera) { const onAfterInput = () => { if ( !prevPositionRef.current || !prevPositionRef.current.equals(camera.position) ) { console.log("change"); setCameraPosition(camera.position.clone()); prevPositionRef.current = camera.position.clone(); } }; // 保存回调引用 const callback = () => onAfterInput(); camera.onAfterCheckInputsObservable.add(callback); return () => { // 使用保存的引用移除回调 camera.onAfterCheckInputsObservable.removeCallback(callback); }; } }, [camera]);
修改后的完整代码示例
import { useState, useEffect, useRef } from 'react'; import { Vector3, Nullable } from '@babylonjs/core'; // 假设scene是已获取的Babylon.js场景实例 const YourComponent = ({ scene }) => { const camera = scene?.cameras[0]; const prevPositionRef = useRef<Nullable<Vector3>>(null); const [cameraPosition, setCameraPosition] = useState<Vector3>( new Vector3(0, 0, 0) ); useEffect(() => { if (camera) { const onAfterInput = () => { if ( !prevPositionRef.current || !prevPositionRef.current.equals(camera.position) ) { console.log("change"); // 传递克隆后的Vector3 setCameraPosition(camera.position.clone()); prevPositionRef.current = camera.position.clone(); } }; // 保存回调引用 const callback = () => onAfterInput(); camera.onAfterCheckInputsObservable.add(callback); // 使用引用移除回调 return () => { camera.onAfterCheckInputsObservable.removeCallback(callback); }; } }, [camera]); // 监听状态变化 useEffect(() => { console.log("cameraPosition updated:", cameraPosition); }, [cameraPosition]); return ( // 你的组件内容 <div>Camera Position: {cameraPosition.x}, {cameraPosition.y}, {cameraPosition.z}</div> ); }; export default YourComponent;
内容的提问来源于stack exchange,提问作者pcace
相关产品推荐
相关产品推荐

