Refs 用法总结

1,044 阅读2分钟

Refs

refs and the dom :

Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。

创建 ref
this.ref = React.createRef();
// hook 函数组件
const ref = useRef(null);
挂载到对应的DOM/组件上
<div ref={this.ref}></div>
// 组件
<Hello ref={this.ref}></Hello>
操作 ref 挂载的
this.ref = React.createRef();
...
<input ref={ref} />
...
this.ref.current.focus();
操作组件下的某个 DOM
const Hello = React.forwardRef((props, ref) => (
	<input ref={ref} />
))
... 
this.ref = React.createRef();
...
<Hello ref={this.ref} />
...
this.ref.current.focus();
自定义暴露实例值
import React, {forwardRef, useImperativeHandle, useState} from 'react';
function Hello(props, ref) {
  const [checked, handleCheck] = useState(false);
  useImperativeHandle(ref, () => ({
    checked: checked
  }), [checked]);
  return <div></div>
}
export default forwardRef(Hello);
...
const ref = useRef(null);
...
<Hello ref={ref} />
...
ref.current.checked; // Hello 中 checked 的值
callback ref (useCallback)

回调refs :

React 也支持另一种设置 refs 的方式,称为“回调 refs”。它能助你更精细地控制何时 refs 被设置和解除。

不同于传递 createRef() 创建的 ref 属性,你会传递一个函数。这个函数中接受 React 组件实例或 HTML DOM 元素作为参数,以使它们能在其他地方被存储和访问。

class CustomTextInput 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();
    };
  }

  componentDidMount() {
    // 组件挂载后,让文本框自动获得焦点
    this.focusTextInput();
  }

  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>
    );
  }
}

函数组件做法如下:

我该如何测量 DOM 节点? :

因为当 ref 是一个对象时它并不会把当前 ref 的值的 变化 通知到我们。使用 callback ref 可以确保 即便子组件延迟显示被测量的节点 (比如为了响应一次点击),我们依然能够在父组件接收到相关的信息,以便更新测量结果。

const AgreeCheckbox = (props, ref) => {
  const [checked, handleCheck] = useState(false);
  useImperativeHandle(ref, () => ({
    checked: checked
  }), [checked]);
  
  return <div></div>
}
export default forwardRef(AgreeCheckbox);
...
const protocolsRef = useCallback(node => {
  if (node !== null) {
    checkedProtocol(node.checked);
  }
}, [])
...
<AgreeCheckbox
  ref={protocolsRef}
  protocolLists={protocolLists}
/>

AgreeCheckbox 是一个 checkbox 组件,当用户点击选择后,useCallback 会得到组件的变化,可以在回调事件中执行一些特殊处理。

此处,useCallback(node => {...}) 很容易让人以为,node 是 useCallback 的参数,但其实不是的。从 useCallback(fn, [deps]) 可知, useCallback 的参数是一个函数,所以 node回调Ref 函数的参数,这里有疑惑的,可以看下 classComponent回调Ref 的实现。

默认情况下,你不能在函数组件上使用 ref 属性,因为它们没有实例。

如果要在函数组件中使用 ref,你可以使用 forwardRef(可与 useImperativeHandle 结合使用),或者可以将该组件转化为 class 组件

在函数组件中,因为没有实例,当函数组件更新时,组件会 rerender 导致 ref 重置了,没有发挥出相应作用。

参考

refs and the dom

useCallback

callback refs