Refs
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>
);
}
}
函数组件做法如下:
因为当 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 重置了,没有发挥出相应作用。