You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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变化

  1. 替换document.querySelector为useRef,确保拿到最新的model-viewer实例:
const mvRef = useRef<HTMLModelViewerElement>(null);
  1. 在组件上绑定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" 
/>
  1. 修改setCameraOrbit函数通过ref操作实例:
function setCameraOrbit(orbit: string = defaultCameraOrbit) {
    mvRef.current?.cameraOrbit = orbit;
}
  1. 用useEffect监听URL变化,在模型更新完成后重置相机:
useEffect(() => {
    setCameraOrbit();
}, [url]);
  1. 简化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 21:18:21