React函数组件更新state后无法调用model-viewer的JavaScript API
解决React中Model-Viewer切换模型后相机轨道不生效的问题
问题描述
在React函数组件中使用@google/model-viewer组件时,切换模型时调用setCameraOrbit重置相机轨道,仅当模型URL未变更时生效;URL变更触发重渲染后,相机轨道无视觉更新,控制台出现警告:
Element model-viewer scheduled an update (generally because a property was set) after an update completed, causing a new update to be scheduled. This is inefficient and should be avoided unless the next update can only be scheduled as a side effect of the previous update. See https://lit.dev/msg/change-in-update for more information.
原代码如下:
export default function RobotDetails() { const [url, setUrl] = useState("../models/forks.glb"); const defaultCameraOrbit = "45deg 55deg 5m"; var mv = document.querySelector("model-viewer"); function changeModel(modelName: string) { setUrl(`../models/${modelName}.glb`); setCameraOrbit(); } function setCameraOrbit(orbit: string = defaultCameraOrbit) { mv!.cameraOrbit = orbit; } return ( ... <div className="modelview-container"> <model-viewer src={new URL(url, import.meta.url).href} camera-orbit="45deg 55deg 4m" camera-controls touch-action="none" interaction-prompt="none" /> <div className="model-select-left"> <button onClick={() => changeModel("table")}>Arm</button> </div> <div className="model-select-right"> <button onClick={() => changeModel("forks")}>Forks</button> <button onClick={() => setCameraOrbit()}>Reset</button> </div> </div> ... ); }
解决方案
方法一:用useRef获取实例+useEffect监听URL变化
- 替换
document.querySelector为useRef,确保拿到最新的model-viewer实例:
const mvRef = useRef<HTMLModelViewerElement>(null);
- 在组件上绑定ref:
<model-viewer ref={mvRef} src={new URL(url, import.meta.url).href} camera-orbit="45deg 55deg 4m" camera-controls touch-action="none" interaction-prompt="none" />
- 修改
setCameraOrbit函数通过ref操作实例:
function setCameraOrbit(orbit: string = defaultCameraOrbit) { mvRef.current?.cameraOrbit = orbit; }
- 用
useEffect监听URL变化,在模型更新完成后重置相机:
useEffect(() => { setCameraOrbit(); }, [url]);
- 简化
changeModel函数,只负责更新URL:
function changeModel(modelName: string) { setUrl(`../models/${modelName}.glb`); }
方法二:利用model-viewer的load事件触发重置
直接在组件上绑定load事件,新模型加载完成后自动重置相机:
<model-viewer ref={mvRef} src={new URL(url, import.meta.url).href} camera-orbit="45deg 55deg 4m" camera-controls touch-action="none" interaction-prompt="none" onLoad={() => setCameraOrbit()} />
这种方式无需额外的useEffect,贴合组件自身生命周期。
问题根源
- 直接
document.querySelector可能拿到旧DOM实例,重渲染后引用失效; setUrl是异步状态更新,调用后立即执行setCameraOrbit时,新模型未完成渲染,修改相机轨道会和组件更新流程冲突,导致警告和不生效。
内容的提问来源于stack exchange,提问作者HuoKnight
相关产品推荐
相关产品推荐

