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

使用forwardRef与useImperativeHandle时双Ref返回null的问题排查

问题分析与解决:useImperativeHandle + forwardRef 导致Ref为null的问题

问题描述

我希望currRef能够访问MyInput组件中的DOM元素,同时让App组件通过inputRef访问该DOM元素。于是我结合使用了useImperativeHandle与forwardRef,但currRef和inputRef均返回null值。请问我哪里出现了错误?

原代码

import React from "react";
import { forwardRef, useRef, useImperativeHandle  } from 'react';
import "./style.css";

export default function App() {
  const childRef = useRef(null);

  function handleClick() {
    console.log('ref ', childRef);
    childRef.current.inputRef.current.focus();
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={childRef} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

const MyInput = forwardRef(function MyInput(props, ref) {
  const { label, ...otherProps } = props;
  const inputRef = useRef(null);
  const currRef = useRef(null);

  console.log('currRef ', currRef.current)

  useImperativeHandle(ref, () => ({
    inputRef,
  }));
  
  return (
    <label>
      {label}
      <input {...otherProps} ref={currRef} />
    </label>
  );
});

错误原因

  1. 初始渲染时ref为null是正常行为:React在组件挂载完成前,不会将DOM元素赋值给ref的current属性。直接在组件顶层打印currRef.current,此时组件还没完成挂载,自然得到null。
  2. 暴露的ref未绑定到DOM元素:你把input的ref绑定给了currRef,但通过useImperativeHandle暴露给父组件的是inputRef——这个ref从未关联任何DOM元素,所以父组件中childRef.current.inputRef.current必然是null。

修正方案

方案1:合并ref(推荐)

不需要同时维护两个ref,直接将inputRef绑定到input元素,同时通过useImperativeHandle暴露给父组件:

import React, { useEffect } from "react";
import { forwardRef, useRef, useImperativeHandle  } from 'react';
import "./style.css";

export default function App() {
  const childRef = useRef(null);

  function handleClick() {
    childRef.current.inputRef.current.focus();
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={childRef} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

const MyInput = forwardRef(function MyInput(props, ref) {
  const { label, ...otherProps } = props;
  const inputRef = useRef(null);

  // 组件挂载完成后打印,即可看到DOM元素
  useEffect(() => {
    console.log('inputRef ', inputRef.current);
  }, []);

  useImperativeHandle(ref, () => ({
    inputRef,
  }));
  
  return (
    <label>
      {label}
      <input {...otherProps} ref={inputRef} />
    </label>
  );
});

方案2:保留两个ref(如果确实需要)

通过回调ref让两个ref同时指向同一个input DOM元素:

import React, { useEffect } from "react";
import { forwardRef, useRef, useImperativeHandle  } from 'react';
import "./style.css";

export default function App() {
  const childRef = useRef(null);

  function handleClick() {
    childRef.current.inputRef.current.focus();
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={childRef} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

const MyInput = forwardRef(function MyInput(props, ref) {
  const { label, ...otherProps } = props;
  const inputRef = useRef(null);
  const currRef = useRef(null);

  useEffect(() => {
    console.log('currRef ', currRef.current);
    console.log('inputRef ', inputRef.current);
  }, []);

  useImperativeHandle(ref, () => ({
    inputRef,
  }));
  
  return (
    <label>
      {label}
      <input 
        {...otherProps} 
        ref={(el) => {
          currRef.current = el;
          inputRef.current = el;
        }} 
      />
    </label>
  );
});

关键修正点

  • 要在useEffect(空依赖数组)中打印ref的current值,确保组件已挂载完成,DOM元素已被赋值。
  • 确保通过useImperativeHandle暴露给父组件的ref,与绑定到input元素的ref是同一个(或指向同一个DOM)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 06:35:21