1:ref的简述
官方文档:在从 render 方法中返回 UI 结构之后,你可能想冲出 React 虚拟 DOM 的限制,在 render 返回的组件实例上调用某些方法。通常来说,这样做对于应用中的数据流动是不必要的,因为活跃的数据( Reactive data )流总是确保最新的 props 被传递到每一个从 render() 输出的子级中去。然而,仍然有几个场景使用这种方式是必须的,或者说是有益的:查找渲染出的组件的DOM标记(可以认为是DOM的标识ID),在一个大型的非React应用中使用React组件或者是将你现有的代码转化成React。
总结下来:ref属性其实就是为了获取DOM节点
2:实战用法
一:给html的标签添加:
question:什么时候回调函数会被被调用呢?
答:当组件挂载后和卸载后,以及ref属性本身发生变化时,回调函数就会被调用。
先举一个通俗易懂的小例子:我们已经把input元素存储在了this.textInput中,在focus函数中直接使用原生DOM API实现focus聚焦。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Input extends Component {
constructor(props){
super(props);
}
focus = () => {
this.textInput.focus();
}
render(){
return (
<div>
<input ref={(input) => { this.textInput = input }} />
</div>
)
}
}
实际开发中:
1)用在input标签上的用法,这里的c值是当前的dom节点,field值是传入的phone值或者smsCode值1
2
3
4
5
6
7forceUpdater = field => (c) => {
if (c) {
c.value = this.state[field]
}
}
<input type='text' placeholder='请输入手机号' ref={this.forceUpdater('phone')} default={phone} data-field='phone' onBlur={this.handleInputChange} />
<input type='text' placeholder='短信验证码' ref={this.forceUpdater('smsCode')} default={smsCode} data-field='smsCode' onBlur={this.handleInputChange} />
2)用在某一个标签上,获取整个dom节点。
这里的this.node为整个dom节点,el为dom节点的最后一个子元素。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21componentDidMount() {
this.setPosition();
}
setPosition() {
const el = this.node.lastChild;
if (!el) return;
const left = (window.innerWidth - el.offsetWidth) / 2;
const top = (window.innerHeight - el.offsetHeight) / 2;
this.setState({ top, left });
}
<div className={cls} ref={node => (this.node = node)}>
{props.modal ? <div className="smartui_mask" style={props.maskStyle && {backgroundColor: 'black'}} onClick={props.modalClose ? this.close : ''} /> : null}
<div className="smartui-layer-content" style={styleObj}>
<div className="smartui-layer-header">
</div>
<div className="smartui-layer-body">
</div>
</div>
</div>
二:给组件添加。
这里的startCounter方法是在Counter组件中定义的方法。注意:1
2
3
4
5
6
7
8
9
10
11
12
13if (res.code === 0) {
this.changeErrorMsg('验证码已发往你的手机,请注意查收')
if (this.counter) {
this.counter.startCounter()
}
} else {
this.changeErrorMsg(res.msg !== '未知错误' ? res.msg : '网络繁忙,请稍后重试')
}
}
{ status === 2 && <div className='row row-smscode'>
<input type='text' placeholder='短信验证码' ref={this.forceUpdater('smsCode')} default={smsCode} data-field='smsCode' onBlur={this.handleInputChange} />
<Counter ref={(c) => { if (c) { this.counter = c } }} onClick={this.checkSendSmsCode} />
</div>}
三:不能在无状态组件中使用ref。
原因很简单,因为ref引用的是组件的实例,而无状态组件准确的说是个函数组件。1
2
3
4
5
6
7
8
9
10
11
12function MyFunctionalComponent() {
return <input />;
}
class Parent extends React.Component {
render() {
return (
<MyFunctionalComponent
ref={(input) => { this.textInput = input; }} />
);
}
}
上面的代码是无法正常工作的。
参考资料:React ref属性使用