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

如何在useMemo中正确使用元素ref?解决初始渲染ref未定义问题

解决方案

方案一:使用useEffect配合useRef保存动画实例

利用useEffect在DOM更新完成后执行的特性,确保sideNavRef已指向真实DOM元素,再创建动画并通过useRef持久化保存实例,避免重复创建。

import { useRef, useEffect } from 'react';

// ... 组件其他逻辑
const sideNavRef = useRef(null);
const animationRef = useRef(null); // 用于持久化保存动画实例

useEffect(() => {
  if (!sideNavRef.current) return;

  // 创建关键帧动画
  const animations = createSideNavKeyframes({
    startingWidth: (7 * document.body.offsetWidth) / 100,
    endingWidth: 55,
  });
  const animation = sideNavRef.current.animate(animations.animation);
  animation.pause();
  animationRef.current = animation;

  // 组件卸载时清理动画,避免内存泄漏
  return () => {
    animation.cancel();
  };
}, []); // 空依赖确保只执行一次
  • 后续可通过animationRef.current调用play()/pause()/reverse()等方法控制动画状态。
  • 清理函数会在组件卸载时取消动画,防止残留实例占用内存。

方案二:使用ref回调函数绑定动画生命周期

将ref赋值与动画创建逻辑合并到回调函数中,精准把控元素挂载/卸载时的动画创建与清理时机。

import { useRef } from 'react';

// ... 组件其他逻辑
const animationRef = useRef(null);

const handleSideNavRef = (element) => {
  // 同步更新sideNavRef指向
  sideNavRef.current = element;

  if (element) {
    // 元素挂载时创建动画
    const animations = createSideNavKeyframes({
      startingWidth: (7 * document.body.offsetWidth) / 100,
      endingWidth: 55,
    });
    const animation = element.animate(animations.animation);
    animation.pause();
    animationRef.current = animation;
  } else {
    // 元素卸载时清理动画
    if (animationRef.current) {
      animationRef.current.cancel();
      animationRef.current = null;
    }
  }
};

// 在nav元素上绑定回调ref
return <nav ref={handleSideNavRef}>...</nav>;
  • 回调函数会在元素挂载(传入DOM节点)和卸载(传入null)时自动触发,逻辑更紧凑。
  • 无需额外依赖useEffect,直接关联元素生命周期。

原useMemo方案失效原因

useMemo的执行时机是组件渲染期间,此时DOM尚未完成更新,sideNavRef.current还未指向真实元素,因此无法创建动画,最终返回undefined。上述两种方案均选择在DOM元素挂载完成后执行动画创建逻辑,从根源解决了ref未定义的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 12:55:18