React Ref

React 支持一个特殊的、可以附加到任何组件上的 ref 属性。

此属性可以是一个对象(React 16.3)、或者一个回调函数、或者一个字符串(遗留 API)。

interface RefObject<T> {
  readonly current: T | null;
}

type RefCallback<T> = { bivarianceHack(instance: T | null): void }["bivarianceHack"];

type LegacyRef<T> = string | RefCallback<T> | RefObject<T> | null;

在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式。要修改一个子组件,你需要使用新的 props 来重新渲染它。但是,在某些情况下,你需要在典型数据流之外强制修改子组件。那么就可以通过 ref 获取到子组件的一个实例,或者一个dom元素。

字符串

用于类组件中,例如设置了ref="inputRef",可以通过 this.refs.inputRef 获取到 DOM 节点/React实例

class StringRef extends React.Component {
  render() {
    return <>
      <input ref="inputRef" />&nbsp;&nbsp;
      <Button ref="btnRef" onClick={() => {
        this.refs.inputRef.value = "Click"
      }}>Click</Button>
    </>
  }

  componentDidMount() {
    console.log(this.refs)
  }
}

注意:不能在函数组件内使用string类型的ref
Function components cannot have string refs. We recommend using useRef() instead.

回调函数

函数中接受 React 组件实例或 HTML DOM 元素作为参数,以使它们能在其他地方被存储和访问。

React 将在组件挂载时,会调用 ref 回调函数并传入 DOM 元素,当卸载时调用它并传入 null。在 omponentDidMountcomponentDidUpdate 触发前,React 会保证 refs 一定是最新的。


class CallbackRef extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;
    
    this.setTextInputRef = element => {
      this.textInput = element;
    };
    
    this.focusTextInput = () => {
      // 使用原生 DOM API 使 text 输入框获得焦点
      if (this.textInput) this.textInput.focus();
    };
  }


  render() {
    // 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React
    // 实例上(比如 this.textInput)
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

Object类型

使用 React.createRef() 创建的,并通过 ref 属性附加到 React 元素。可以通过实例的current属性对DOM节点或者组件实例进行访问。

class ObjectRef extends React.Component {
  constructor(props) {
    super(props);
    // 创建一个 ref 来存储 textInput 的 DOM 元素
    this.textInput = React.createRef();
  }

  focusTextInput = () => {
    // 直接使用原生 API 使 text 输入框获得焦点
    // 注意:我们通过 "current" 来访问 DOM 节点
    this.textInput.current.focus();
  }

  render() {
    // 告诉 React 我们想把 <input> ref 关联到
    // 构造器里创建的 `textInput` 上
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

注意:默认情况下,不能在函数组件上使用ref属性,因为函数组件没有实例。
Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()

在函数组件上使用Ref

forwardRef,它可以将 ref自动地通过组件传递到子组件。作为子组件第二个参数。

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

搭配 useImperativeHandle 一起使用,暴露给调用者只能使用的方法

  useImperativeHandle(ref, () => ({
    validate: () => {
      return new Promise((resolve, reject) => {
        field.validate((errors, values: any) => {
          if (errors) return reject(errors);
          // TODO
          return resolve(values$);
        });
      });
    }
  }), []);

createRef 和 useRef 区别

  1. createRef 常用于类组件中,useRef 只能用于函数组件
  2. useRef返回一个可变的ref对象,其.current属性被初始化为传入的参数(initialValue)。返回的对象将在组件的整个生命周期内持续存在。始终是同一个对象。
function useRef<T>(initialValue: T|null): RefObject<T>;
function useRef<T = undefined>(): MutableRefObject<T | undefined>;
// 保存上一次渲染时的状态内容
function usePrevious<T>(state: T, compare?: compareFunction<T>): T | undefined {
  const prevRef = useRef<T>();
  const curRef = useRef<T>();

  const needUpdate = typeof compare === 'function' ? compare(curRef.current, state) : true;
  if (needUpdate) {
    prevRef.current = curRef.current;
    curRef.current = state;
  }

  return prevRef.current;
}

可以发现 useRef 的用途比 ref更加广泛,它可以存储任意的 JavaScript值而不仅仅是 DOM 引用

推荐

《Introduction to useRef Hook》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青菜小王子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值