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

React中如何在ref.current首次可用时执行初始化操作?

解决方案:优雅处理@react-three/fiber中CameraControls的初始化问题

当然可以实现这样的自定义Hook,同时还有几种适配@react-three/fiber场景的优雅方案,彻底替代定时器的临时 workaround:

方案1:自定义useRefWithInitialCallback Hook

这个Hook会在ref的current属性首次被赋值为非null值时,自动执行你传入的回调函数,完美适配React组件的ref赋值时机。

import { useRef, useState, useCallback } from 'react';

function useRefWithInitialCallback<T>(callback: (ref: T) => void) {
  const ref = useRef<T>(null);
  const [hasTriggered, setHasTriggered] = useState(false);

  const callbackRef = useCallback((instance: T | null) => {
    ref.current = instance;
    // 仅在首次赋值非null实例时执行回调
    if (instance && !hasTriggered) {
      callback(instance);
      setHasTriggered(true);
    }
  }, [callback, hasTriggered]);

  return [ref, callbackRef] as const;
}

在你的组件中使用:

const [cameraRef, cameraCallbackRef] = useRefWithInitialCallback<CameraControls>((controls) => {
  controls.setLookAt(
    ...cameraInitialPosition,
    ...cameraInitialTarget,
    true
  );
});

return (
  <Canvas shadows>
    <CameraControls ref={cameraCallbackRef} />
    {/* 其他3D内容 */}
  </Canvas>
);

这里使用回调式ref替代普通ref,React会在CameraControls组件完成挂载、ref实例赋值时自动调用cameraCallbackRef,直接在回调内完成初始化逻辑,完全避开了useEffect时机不匹配的问题。

方案2:利用@react-three/fiber的useFrame

结合Three.js的渲染帧循环,在每帧检测ref是否就绪,一旦就绪就执行初始化并停止监听:

import { useFrame } from '@react-three/fiber';

const cameraRef = useRef<CameraControls>(null);
const [isInitialized, setIsInitialized] = useState(false);

useFrame(() => {
  if (!isInitialized && cameraRef.current) {
    cameraRef.current.setLookAt(
      ...cameraInitialPosition,
      ...cameraInitialTarget,
      true
    );
    setIsInitialized(true);
  }
});

return (
  <Canvas shadows>
    <CameraControls ref={cameraRef} />
    {/* 其他3D内容 */}
  </Canvas>
);

useFrame会在Three.js场景的每帧渲染时执行,确保能精准捕捉到CameraControls实例就绪的时机,不会出现过早执行的问题。

方案3:监听CameraControls的ready事件

drei封装的CameraControls组件自带onReady事件,会在控件完全初始化完成后触发,这是最贴合组件本身的解决方案:

const cameraRef = useRef<CameraControls>(null);

const handleReady = () => {
  if (cameraRef.current) {
    cameraRef.current.setLookAt(
      ...cameraInitialPosition,
      ...cameraInitialTarget,
      true
    );
  }
};

return (
  <Canvas shadows>
    <CameraControls ref={cameraRef} onReady={handleReady} />
    {/* 其他3D内容 */}
  </Canvas>
);

内容的提问来源于stack exchange,提问作者Dusan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 00:05:15