# React Hooks useRef 使用范围

作者:HerryLo (opens new window)

博客原文链接 (opens new window)

React Class组件中的createRef,与React Hooks函数组件中的useRef相对应。在说useRef之前,就不得不先看下createRef这个API的目的,它是为了解决什么问题?这样我们才能更加的了解 useRef API。

# 了解 createRef API

React.createRef 可以很好的帮助开发者,获取到DOM元素节点实例,也可以帮助我们获取Class组件的组件实例。对于 createRef API 适用情况,在官方文档中已有给出:

下面是几个适合使用 refs 的情况:

  • 管理焦点,文本选择或媒体播放。
  • 触发强制动画。
  • 集成第三方 DOM 库。

避免使用 refs 来做任何可以通过声明式实现来完成的事情。

// 通过创建createRef,控制input元素节点DOM
class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // 创建一个 ref 来存储 textInput 的 DOM 元素
    this.textInput = React.createRef();
    this.focusTextInput = this.focusTextInput.bind(this);
  }

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

// 通过创建createRef,控制子class组件this实例
class AutoFocusTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }

  componentDidMount() {
    this.textInput.current.focusTextInput();
  }

  render() {
    return (
        { /* CustomTextInput 必须是class组件才有效 */ }
      <CustomTextInput ref={this.textInput} />
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

以上就是 createRefAPI 的典型使用方法,不过有点需要注意,通过React.createRef创建的实例,不能挂在函数组件上,因为它们没有实例。如果一定要使用,可以通过props参数的形式进行传递。

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  render() {
    return (
      <CustomTextInput
        inputRef={el => this.inputElement = el}
      />
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# useRef 使用

useRef的主要功能与createRef类似,通过声明 useRef变量而后赋值访问DOM节点。不过相对于createRefuseRef功能更加全面和多样。

# 访问DOM节点

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  // `current` 指向已挂载到 DOM 上的文本输入元素
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13

通过ref绑定访问DOM节点,DOM节点依然是挂载在current上。这依然是它的核心功能;

# 保存任何可变值

function Timer() {
  const intervalRef = useRef();

  useEffect(() => {
    const id = setInterval(() => {
      // ...
    });
    intervalRef.current = id;
    return () => {
      clearInterval(intervalRef.current);
    };
  });
  
  function handleCancelClick() {
    clearInterval(intervalRef.current);
  }
  
  return (
  	// ...
  )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

useRef对象是一个 current 属性可变且可以容纳任意值的通用容器。请记住,当 ref 对象内容发生变化时,useRef 并_不会_通知,而且每次渲染时都会返回同一个 ref 对象

# ref 回调

function MeasureExample() {
  const [height, setHeight] = useState(0);

  const measuredRef = useCallback(node => {
    if (node !== null) {
      setHeight(node.getBoundingClientRect().height);
    }
  }, []);

  return (
    <>
      <h1 ref={measuredRef}>Hello, world</h1>
      <h2>The above header is {Math.round(height)}px tall</h2>
    </>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

通过useCallback传递依赖为 [],保证当且仅当组件挂载和卸载时,callback ref 才会被调用。

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

createRefuseRef 存在明显的差异,不过主体功能基本一致,useRef不止可访问DOM阶段,还可以在 current 属性存储任意变量而不会被渲染修改,ref 回调没有大的变化,依然是通过传入函数,是 ref 功能扩大化,可以做更多的事情。

🥰Me

男性,武汉工作,会点Web,擅长Javascript.

技术或工作问题交流,可联系微信:1169170165.

🚀小程序&公众号
🚀运行
2024年2月19日星期一下午2点01分
博客已运行:--天--时--分--秒
上次更新: 7/19/2023, 2:00:31 PM

评 论: