面试题:React 中如何获取组件对应的 DOM 元素?

在 React 中,直接操作 DOM 通常不被推荐(应优先考虑状态驱动 UI),但在某些特定场景下(如管理焦点、触发动画、集成第三方库)需要访问真实的 DOM 元素。React 提供了 ref 来实现这一需求。

以下是几种获取组件对应 DOM 元素的方法:


1. 使用 useRef Hook (函数组件推荐)

useRef 是函数组件中创建 ref 的标准方式。它返回一个可变的 ref 对象,其 .current 属性指向 DOM 元素。

基本用法:

import React, { useRef, useEffect } from 'react';

function TextInput() {
  const inputRef = useRef(null);

  const focusInput = () => {
    // 访问真实 DOM 元素并调用其方法
    inputRef.current.focus();
  };

  return (
    <div>
      {/* 将 ref 绑定到 DOM 元素 */}
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>聚焦输入框</button>
    </div>
  );
}

结合 useEffect 在挂载后操作 DOM:

function AutoFocusInput() {
  const inputRef = useRef(null);

  useEffect(() => {
    // 组件挂载后自动聚焦
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} type="text" />;
}

2. 使用 createRef (类组件)

在类组件中,可以使用 React.createRef() 创建 ref。

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    // 创建 ref
    this.myRef = React.createRef();
  }

  componentDidMount() {
    // 访问 DOM 元素
    this.myRef.current.focus();
  }

  render() {
    return <input ref={this.myRef} type="text" />;
  }
}

3. 回调 Refs (更灵活的控制)

回调 ref 允许你获得对 ref 创建和销毁的完全控制。当组件挂载时,React 会调用回调函数并传入 DOM 元素;卸载时传入 null

function CustomTextInput() {
  let inputElement;

  const setRef = (element) => {
    inputElement = element;
    // 可在此处执行其他逻辑
  };

  const focus = () => {
    inputElement.focus();
  };

  return (
    <div>
      <input type="text" ref={setRef} />
      <button onClick={focus}>聚焦</button>
    </div>
  );
}

注意:如果将内联函数作为 ref 回调,React 会在更新时调用两次(先传 null,再传 DOM 元素)。为避免性能问题,建议使用 useCallback 或类属性语法稳定函数引用。


4. 转发 Ref (React.forwardRef) 获取子组件 DOM

默认情况下,ref 不能用于函数组件。若想让父组件访问子组件内部的 DOM 元素,需使用 React.forwardRef

// 子组件:使用 forwardRef 暴露内部 DOM
const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="fancy-button">
    {props.children}
  </button>
));

// 父组件:通过 ref 获取子组件内的 button 元素
function Parent() {
  const buttonRef = useRef(null);

  const clickButton = () => {
    buttonRef.current.click(); // 触发子组件按钮的点击
  };

  return (
    <div>
      <FancyButton ref={buttonRef}>点击我</FancyButton>
      <button onClick={clickButton}>通过父组件点击</button>
    </div>
  );
}

5. 使用 findDOMNode (已废弃,不推荐)

在旧版 React 中,findDOMNode 可用于查找组件对应的 DOM 节点。但该方法:

  • 已被废弃,不推荐在新代码中使用。
  • 不支持严格模式。
  • 无法与函数组件或 React.memo 一起使用。
// ❌ 不推荐
// const domNode = findDOMNode(this);

总结对比

方法适用场景备注
useRef函数组件访问自身 DOM最常用,推荐
createRef类组件访问自身 DOM类组件标准方式
回调 Refs需要精确控制 ref 生命周期更灵活,但稍复杂
forwardRef父组件访问子组件内部 DOM解决 ref 透传问题
findDOMNode❌ 已废弃避免使用

注意事项

  1. 不要过度使用 ref:优先通过 state 和 props 控制组件行为。
  2. ref 的值在渲染期间不可变useRef 返回的对象在整个组件生命周期中保持不变。
  3. DOM 节点在挂载后才可用:确保在 useEffectcomponentDidMount 后再访问 ref.current
  4. TypeScript 中需正确声明类型const inputRef = useRef<HTMLInputElement>(null);

掌握这些方法,就能在必要时安全、有效地访问和操作 DOM 元素。

THE END
喜欢就支持一下吧
点赞13 分享