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

React动态分配useRef触发无效Hook调用错误求助

解决React动态分配Refs的Hook调用错误

错误原因

你在useEffect的异步函数循环中调用useRef(),违反了React Hook的核心规则:所有Hook必须在函数组件的顶层作用域调用,不能在循环、条件判断、嵌套函数(比如forEach回调、async函数内部)中执行Hook。

修复方案

我们不需要在useEffect里创建Hook,而是利用顶层已有的planetRefs(通过useRef创建的容器)来动态存储每个Planet组件的引用,同时修正ref的匹配逻辑:

步骤1:移除错误的Hook调用

删除useEffect里的以下代码:

fetchedSubsistemaPlanetario.forEach((planeta) => {
  const camelCaseSlug = planeta.padre.slug.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
  planetRefs.current[camelCaseSlug] = useRef(); // 删掉这行
});

步骤2:在渲染时动态分配Ref

修改组件的渲染逻辑,统一用camelCaseSlug作为ref的键(避免index作为key的不稳定性),同时通过回调ref直接存储组件引用:

export const SubsistemaPlanetario = function SubsistemaPlanetario(props) {
  let running = true;
  let stopRunning = () => (running = false);
  let startRunning = () => (running = true);

  const [subsistemaPlanetario, setSubsistemaPlanetario] = useState([]);
  const planetRefs = useRef({}); // 顶层的ref容器保持不变

  useEffect(() => {
    async function fetchSubsistemaPlanetario() {
      try {
        const fetchedSubsistemaPlanetario = await getSubsistemaPlanetario();
        setSubsistemaPlanetario(fetchedSubsistemaPlanetario);
        // 这里不再处理ref,把ref逻辑移到渲染阶段
      } catch (error) {
        console.error(error);
      }
    }

    fetchSubsistemaPlanetario();
  }, []);

  return (
    <>
      {subsistemaPlanetario.map((planetaPadre) => {
        // 生成唯一的驼峰键
        const camelCaseSlug = planetaPadre.padre.slug.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
        return (
          <Planet
            key={camelCaseSlug} // 用唯一的slug作为key,不要用index
            scale={0.5}
            // 回调ref:挂载时存储组件实例,卸载时设为null
            ref={(planetInstance) => {
              planetRefs.current[camelCaseSlug] = planetInstance;
            }}
            stopRunning={stopRunning}
            startRunning={startRunning}
            textureType="haumea"
            linkTo="areas"
            linkToLabel="Areas"
          />
        );
      })}
    </>
  );
};

为什么这样可行?

  • planetRefs是在组件顶层通过useRef创建的稳定容器,它的current属性可以动态修改,不受渲染周期影响。
  • 回调ref会在Planet组件挂载时自动把实例传入回调函数,我们直接把实例存入planetRefs.current的对应键中,完全符合React的规则。
  • 替换index为camelCaseSlug作为key,确保组件更新时ref能正确匹配对应的Planet实例。

验证使用

之后你可以通过planetRefs.current[camelCaseSlug]访问对应的Planet组件实例,比如在useFrame或者其他顶层Hook中操作:

useFrame((state, delta) => {
  // 示例:访问某个星球的实例并修改旋转
  if (planetRefs.current.haumea) {
    planetRefs.current.haumea.rotation.y += 0.01 * delta;
  }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 16:49:58