React Ref 其实是这样的

ref 的由来在典型的 React 数据流中 , props 是父组件与子组件交互的唯一方式 。 要修改一个子组件 , 你需要使用新的 props 来重新渲染它 。 但是 , 在某些情况下 , 你需要在典型数据流之外强制修改子组件/元素 。
适合使用 refs 的情况:

  • 管理焦点 , 文本选择或媒体播放 。
  • 触发强制动画 。
  • 集成第三方 DOM 库 。
ref 的四种方式在 React v16.3 之前 , ref 通过字符串(string ref)或者回调函数(callback ref)的形式进行获取 。
ref 通过字符获取:
// string refclass MyComponent extends React.Component {componentDidMount() {this.refs.myRef.focus();}render() {return ;}}ref 通过回调函数获取:
// callback refclass MyComponent extends React.Component {componentDidMount() {this.myRef.focus();}render() {return {this.myRef = ele;}} />;}}在 v16.3 中 , 经 0017-new-create-ref 提案引入了新的 API:React.createRef 。
ref 通过 React.createRef 获取:
// React.createRefclass MyComponent extends React.Component {constructor(props) {super(props);this.myRef = React.createRef();}componentDidMount() {this.myRef.current.focus();}render() {return ;}}当然还有最近react大力推崇的 hooks:useRef
function MyComponent() {const myRef = useRef(null);const onButtonClick = () => {// `current` 指向已挂载到 DOM 上的文本输入元素myRef.current.focus();};return (<>聚焦);}将被移除的 string ref首先来具体说说 string ref , string ref 就已被诟病已久 , React 官方文档中如此声明:"如果你目前还在使用 this.refs.textInput 这种方式访问 refs, 我们建议用回调函数或 createRef API 的方式代替 。 " , 为何如此糟糕?
最初由 React 作者之一的 dan abramov 。 发布于 , (该网站需要梯子) 。 吐槽内容主要有以下几点:
  1. string ref 不可组合 。例如一个第三方库的父组件已经给子组件传递了 ref , 那么我们就无法在在子组件上添加 ref 了 。另一方面 , 回调引用没有一个所有者 , 因此您可以随时编写它们 。 例如:
/** string ref **/class Parent extends React.Component {componentDidMount() {// 可获取到 this.refs.childRefconsole.log(this.refs);}render() {const { children } = this.props;return React.cloneElement(children, {ref: 'childRef',});}}class App extends React.Component {componentDidMount() {// this.refs.child 无法获取到console.log(this.refs);}render() {return ();}}
  1. string ref 的所有者由当前执行的组件确定 。这意味着使用通用的“渲染回调”模式(例如react) , 错误的组件将拥有引用(它将最终在react上而不是您的组件定义renderRow) 。
class MyComponent extends Component {renderRow = (index) => {// string ref 会挂载在 DataTable this 上return ;// callback ref 会挂载在 MyComponent this 上return this['input-' + index] = input} />;}render() {return }}
  1. string ref 不适用于Flow之类的静态分析 。Flow不能猜测框架可以使字符串ref“出现”在react上的神奇效果 , 以及它的类型(可能有所不同) 。回调引用比静态分析更友好 。
  2. string ref 强制React跟踪当前正在执行的组件 。这是有问题的 , 因为它使react模块处于有状态 , 并在捆绑中复制react模块时导致奇怪的错误 。 在 reconciliation 阶段 , React Element 创建和更新的过程中 , ref 会被封装为一个闭包函数 , 等待 commit 阶段被执行 , 这会对 React 的性能产生一些影响 。
关于这点可以参考 React 源码 coerceRef 的实现:
在调和子节点得过程中 , 会对 string ref 进行处理 , 把他转换成一个方法 , 这个方法主要做的事情就是设置 instance.refs[stringRef] = element , 相当于把他转换成了function ref
对于更新得过程中string ref是否变化需要对比得是 current.ref._stringRef , 这里记录了上一次渲染得时候如果使用得是string ref他的值是什么
owner是在调用createElement的时候获取的 , 通过ReactCurrentOwner.current获取 , 这个值在更新一个组件前会被设置 , 比如更新ClassComponent的时候 , 调用render方法之前会设置 , 然后调用render的时候就可以获取对应的owner了 。
坚挺的 callback refReact 将在组件挂载时 , 会调用 ref 回调函数并传入 DOM 元素 , 当卸载时调用它并传入 null 。 在 componentDidMount 或 componentDidUpdate 触发前 , React 会保证 refs 一定是最新的 。