09-ref/creatRef-非受控

221 阅读2分钟

上节回顾:

受控:简单来说就是依赖唯一状态源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连接起来呢?使得成为非受控组件。

  1. 通过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} /> //注意
            )
        }
    }
  1. 回调函数
 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} /> //注意
            )
        }
    }
  1. 通过“中介”来转发
 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}/> //注意
            )
        }
    }