上节回顾:
受控:简单来说就是依赖唯一状态源state,由React自动控制的表单元素称为“受控组件”。且无需操作真实DOM
非受控:绑定事件使用 event 对象(点击事件/监听事件);使用 ref 属性等等。
分析源码:
var emptyObject = {}
function Component(props, context, updater) {
// 挂载到父组件的实例
this.props = props;
this.refs = emptyObject;
}
解释:在component类组件中,创建了一个空对象,也就是在堆内存中开辟了一个新的空间。在方法中,有三个属性被挂载到了父组件React.component实例上,其中有一个refs属性,指向空对象{}。
refs的使用:
说道,refs指向空对象,也就是说它相当于一个容器。在组件中将真实DOM放在refs引用容器中。
过程:
vDOM = ReactDOM.render(vDOM)
ReactDOM检查是否有ref属性在jsx身上,如果有,就会把属性所在的真实DOM放入this.refs引用容器{}里。
容器存储的形式为键值对形式,{key:value},其中Key为ref属性所对应的值,value为ref所在的真实DOM。
<h2 ref="time">{new Date().toLocalTimeString()}</h2>
上面代码中,key为time,value为h2。如果想要改变当前时间的话,那么就在钩子函数中:
this.refs.time.innerHTML = new Date().toLocalTimeString()
就可以操作真实dom改变事件了。
如果ref="一个函数",那么会怎样呢,试试看。
<h2 ref={(element) => {this.timeRef = element }}>
{new Date().toLocaleTimeString()}
</h2>
重新分析,当ReactDOM检查到有ref属性的时候,如果不是函数,就会把ref所在的真实dom放入this.refs引用的容器{}里;但检查到如果是函数的话,那就先获取真实DOM,执行函数,并把真实DOM传给该函数。
即:
this.timeRef.innerHTML = new Date().toLocaleTimeString()
成功修改时间。
creatRef的使用:
源码分析:
function creatRef(){
var refObject = {
current:null //设置和取值的key一致
}
{
Object.seal(refObject)
}
return refObject;
}
其中seal的含义为封闭,也就是说,将这个对象封闭住,不允许这个对象还有其他的属性。
使用:先在react中找到creatRef()这个方法,并挂载在this实例上。
timeRef = React . creatRef() //实质为current:{}
然后:
<h2 ref={this.timeRef}>{new Date().toLocalTimeString()}</h2>
获取到真实dom后修改:
this.timeRef.current.innerHTML = new Date().toLocaleTimeString()
修改成功。
进阶问题:涉及父子组件
如何在父子组件中也使用ref来进行真实DOM的使用呢?
比如如下场景:
function ChildInput(props) {
return (
<div>
<input type="text" />
</div>
)
}
return(
<ChildInput />
)
那么如何在父组件与子组件中ref连接起来呢?使得成为非受控组件。
- 通过props对象引用的方式
相当于把descRef挂载到props上,然后调用。
function ChildInput1(props) {
return (
<div>
<input type="text" ref={props.descRef} /> //注意
</div>
)
}
class App extends React.Component{
descRef = React.createRef() //注意
render(){
return(
<ChildInput1 descRef={this.descRef} /> //注意
)
}
}
- 回调函数
function ChildInput2(props) {
return (
<div>
<input type="text" ref={this.name} /> //注意
</div>
)
}
callback=(e)=>{ //注意
this.name = e
}
class App extends React.Component{
render(){
return(
<ChildInput2 callback={this.callback} /> //注意
)
}
}
- 通过“中介”来转发
function ChildInput3(props,ref) { //注意
return (
<div>
<input type="text" ref={ref} /> //注意
</div>
)
}
const RefChild = React.forwardRef(ChildInput3) //注意
class App extends React.Component{
descRef = React.createRef() //注意
render(){
return(
<RefChild ref={this.descRef}/> //注意
)
}
}