什么是 ref
ref 就是 reference(引用),当我们希望直接使用 DOM 元素中的某个方法(例如许多 H5 的 API)或者希望直接使用自定义组件中的某个方法。
先来看看 ref 大致怎么使用,可以很明了的看出 ref 就是组件的一个属性:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class App extends React.Component { componentDidMount() { console.log(this); }
render() { return ( <div> <h1 ref="text">hello world</h1> </div> ); } }
|

ref 的使用组件要求:
-
ref 作用于内置的 html 组件,得到的将是真实的 DOM 对象
-
ref 作用于类组件,得到的将是类的实例
-
ref 不能作用于函数组件
ref 的用法
就像上面的例子,可以直接给一个 字符串,则 ref 则会成为类组件的 refs 属性的属性,传入的字符串则为 key,但是不推荐这种用法,即将被官方淘汰。而且,函数组件无法拥有这种类型的 ref。

对象
通过 React.createRef 函数创建,该函数返回一个对象,然后将该对象传给给组件的 ref 属性。
1 2 3 4 5 6 7 8 9 10 11 12 13
| const ref = React.createRef();
function App() { setTimeout(() => { console.log(ref); }, 0);
return ( <div> <h1 ref={ref}>hello world</h1> </div> ); }
|
其实 React.createRef 函数做的事很简单,就是创建了一个 { current: null } 样子的对象,最后 current 属性值会变成我们需要的值。
其实我们不使用该函数,直接给 ref 变量赋值为一个这样的字面量也可以。

函数
给 ref 属性传入一个函数,该函数接收一个参数,该参数即为我们需要的值(DOM 对象 / 组件实例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class CmpA extends React.Component { render() { return <div>CmpA</div>; } }
function App() { let refA = null;
setTimeout(() => { console.log(refA); }, 0);
return ( <div> <CmpA ref={(el) => (refA = el)} /> </div> ); }
|

函数调用的时间
在类组件的生命周期中调用时间
-
componentDidMount 的之前会调用该函数
在 componentDidMount 事件中可以使用 ref
-
如果 ref 的值发生了变动(旧的函数被新的函数替代),分别调用旧的函数以及新的函数,时间点出现在componentDidUpdate之前
-
旧的函数被调用时,传递 null
-
新的函数被调用时,传递对象
-
如果 ref 所在的组件被卸载,会调用函数
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
| class App extends React.Component { state = { show: true, };
componentDidMount() { console.log("componentDidMount", this.text); }
render() { const { show } = this.state; const showContent = show ? ( <h1 ref={(el) => { console.log("ref", el); this.text = el; }} > hello world </h1> ) : null; return ( <div> {showContent} <button onClick={() => this.setState({})}>改变函数</button> <button onClick={() => this.setState({ show: !show, }) } > 显示/隐藏 </button> </div> ); } }
|

ref 转发
有些时候我们并不需要直接使用 ref,而是想传递给子组件,特别是使用了 HOC 的情况下,HOC 不能污染子组件,这个时候直接传递 ref,就不能达到我们的要求,就需要 ref 转发(ref forward)
使用 ref 转发需要使用 React.forwardRef 函数,该函数接收一个函数组件,函数组件有两个参数(props,ref),在函数组件中我们可以控制 ref 转发给是谁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const Cmp = React.forwardRef((props, ref) => <h1 ref={ref}>hello world</h1>);
class App extends Component { ref = React.createRef();
componentDidMount() { console.log(this.ref); }
render() { return ( <div> <Cmp ref={this.ref} /> </div> ); } }
|

在 HOC 中使用的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function withLog(Comp) { class LogWrapper extends React.Component { componentDidMount() { console.log(`日志:组件${Comp.name}被创建了!${Date.now()}`); } componentWillUnmount() { console.log(`日志:组件${Comp.name}被销毁了!${Date.now()}`); } render() { const { forwardRef, ...rest } = this.props; return ( <> <Comp ref={forwardRef} {...rest} /> </> ); } }
return React.forwardRef((props, ref) => { return <LogWrapper {...props} forwardRef={ref} />; }); }
|