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

React TS中父组件Ref传子组件时出现.current不存在的TS错误

父组件Ref传递给子组件的TypeScript类型问题及解决示例

把App组件的mainContainer ref传给Navbar子组件后,访问mainContainer.current触发TypeScript错误:

Property 'current' does not exist on type '((instance: unknown) => void) | MutableRefObject'.
Property 'current' does not exist on type '(instance: unknown) => void'.ts(2339)

实际属性存在且能正常访问,但类型识别存在问题。

问题原因

React的Ref类型包含两种形式:MutableRefObject(即useRef创建的带current属性的对象)和回调函数式ref。forwardRef的第二个参数ref默认类型为Ref<T> | undefined,TypeScript无法确定传入的ref一定是带current的对象,因此触发报错。


方案1:通过Props传递父组件Ref(推荐)

forwardRef的设计初衷是子组件向父组件暴露自身DOM节点/组件实例,反向传递父组件的ref更适合用普通props,语义清晰且类型易处理。

父组件(App.tsx)

import Navbar from "./components/Navbar.tsx"
import app from "./App.module.css"
import { useRef } from "react"

function App() {
  const mainContainer = useRef<HTMLDivElement | null>(null);

  return (
    <>
      <div id={app.mainContainer} ref={mainContainer}>
        <button className="nav-btn" id="open-nav-btn">Menu</button>
        {/* 通过props传递父组件ref */}
        <Navbar mainContainerRef={mainContainer} />
      </div>
    </>
  )
}

export default App

子组件(Navbar.tsx)

import "./navbar.css"
import { gsap } from "gsap";
import { useGSAP } from "@gsap/react";
import { useRef } from "react";
import { ExpoScaleEase } from "gsap/EasePack";
gsap.registerPlugin(useGSAP, ExpoScaleEase);

// 定义Props类型,明确ref的类型
type NavbarProps = {
  mainContainerRef: React.MutableRefObject<HTMLDivElement | null>;
};

const Navbar = ({ mainContainerRef }: NavbarProps) => {
  const navContainer = useRef<HTMLDivElement>(null);
  
  const { contextSafe } = useGSAP({ scope: navContainer });

  const closeNav = contextSafe(() => {
    const duration: number = 0.4;

    // 类型明确,可安全访问current
    if (mainContainerRef.current && navContainer.current) {
      gsap.set(
        mainContainerRef.current,
        { overflow: "auto" }
      );  

      gsap.to(
        navContainer.current,
        { opacity: 0, ease: "power2.in", duration }
      );

      gsap.set(
        navContainer.current,
        { clipPath: "inset(100% 0% 0% 0%)", delay: duration }
      );

      gsap.set(
        navContainer.current,
        { display: "none", delay: duration }
      );
    }
  });

  return (
    <div id="nav-container" ref={navContainer}>
      <button className="nav-btn" id="close-nav-btn" onClick={closeNav}>Close</button>
      <div className="nav-wrapper">
        <nav>
          <ul className="nav-pages">
            <li>HOME</li>
            <li>ABOUT</li>
            <li>CONTACT</li>
            <li>PROJECTS</li>
          </ul>
          <hr />
          <ul className="nav-socials">
            <li>Instagram</li>
            <li>Linkedin</li>
            <li>Github</li>
          </ul>
        </nav>
        <p>Mirko Bellopede <br /> Full stack web developer Portfolio 2024 ©</p>
      </div>
    </div>
  );
};

export default Navbar

方案2:使用forwardRef并明确类型(不推荐,语义不符)

如果一定要用forwardRef传递父组件的ref,需明确ref的类型并添加类型断言:

修改后的子组件(Navbar.tsx)

import "./navbar.css"
import { gsap } from "gsap";
import { useGSAP } from "@gsap/react";
import { forwardRef, useRef } from "react";
import { ExpoScaleEase } from "gsap/EasePack";
gsap.registerPlugin(useGSAP, ExpoScaleEase);

// 明确forwardRef的泛型类型为MutableRefObject
const Navbar = forwardRef<React.MutableRefObject<HTMLDivElement | null>, {}>(function Navbar(props, ref) {
  const navContainer = useRef<HTMLDivElement>(null);
  // 类型断言,告知TS该ref为带current的对象
  const mainContainer = ref as React.MutableRefObject<HTMLDivElement | null>;
  
  const { contextSafe } = useGSAP({ scope: navContainer });

  const closeNav = contextSafe(() => {
    const duration: number = 0.4;

    if (mainContainer.current && navContainer.current) {
      gsap.set(
        mainContainer.current,
        { overflow: "auto" }
      );  

      gsap.to(
        navContainer.current,
        { opacity: 0, ease: "power2.in", duration }
      );

      gsap.set(
        navContainer.current,
        { clipPath: "inset(100% 0% 0% 0%)", delay: duration }
      );

      gsap.set(
        navContainer.current,
        { display: "none", delay: duration }
      );
    }
  });

  return (
    <div id="nav-container" ref={navContainer}>
      <button className="nav-btn" id="close-nav-btn" onClick={closeNav}>Close</button>
      <div className="nav-wrapper">
        <nav>
          <ul className="nav-pages">
            <li>HOME</li>
            <li>ABOUT</li>
            <li>CONTACT</li>
            <li>PROJECTS</li>
          </ul>
          <hr />
          <ul className="nav-socials">
            <li>Instagram</li>
            <li>Linkedin</li>
            <li>Github</li>
          </ul>
        </nav>
        <p>Mirko Bellopede <br /> Full stack web developer Portfolio 2024 ©</p>
      </div>
    </div>
  );
});

export default Navbar

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 11:05:22