web 杂谈React Native开发Web前端之路

弄懂 React 的 ref,理解 React 的实例与 vDo

2017-09-08  本文已影响862人  高少辉_骚辉

React 的 ref

最近公司来了一个新人,然后在使用 ref 的时候,碰到了一些问题,帮她解答后现在拿出来分享一下。

当你给组件H5标签添加 ref 属性后,此实例只能在当前组件中被访问到,父组件的 refs 中是没有此引用的,例如:

import React, { Component } from 'react'
import { render } from 'react-dom'
import util from './lib/util'
export default class Father extends Component {

  componentDidMount () {
    console.log( this.refs['child-h1'] ) // => undefined
  }

  render () {
    
    return <div>
      <ChildComponent ></ChildComponent>
    </div>
  }
}

class ChildComponent extends Component {

  componentDidMount () {
    console.log( this.refs['child-h1'] ) // => h1 可以拿到这个 h1
  }

  render () {
    return <div>
      <h1 ref="child-h1">我是子组件</h1>
    </div>
  }
}

render(<Father />, document.getElementById("root"))

从上面例子,就可以知道 React 的 ref 只针对本组件有效,那如果我想使用父组件访问子组件的 ref 怎么办?看例子:

import React, { Component } from 'react'
import { render } from 'react-dom'
import util from './lib/util'
export default class Father extends Component {

  componentDidMount () {
    console.log( this.refs['child-h1'] ) // => undefined
    console.log( this.refs['child-component'].refs['child-h1'] ) // => h1 可以拿到这个 h1  
    }

  render () {
    
    return <div>
      <ChildComponent ref="child-component"></ChildComponent>
    </div>
  }
}

class ChildComponent extends Component {

  componentDidMount () {
    console.log( this.refs['child-h1'] ) // => h1 可以拿到这个 h1
  }

  render () {
    return <div>
      <h1 ref="child-h1">我是子组件</h1>
    </div>
  }
}

render(<Father />, document.getElementById("root"))

而细心的小伙伴就会发现,这边我们的 ref 指向的是一个 React 组件,不是 h5 标签。我们都知道给 h5 元素设置 ref 后,我们可以拿到它的真实 dom,那如果我们给 React 组件 加了一个 ref,考虑过会得到什么吗?虚拟 dom?其实并不是,我们拿到是 React 组件 的实例,这和 vDom 有什么差别?我相信很多人会疑惑这个,下面我们就介绍一波

React 的实例与 vDom(虚拟 dom)之间的关系

首先我们都知道 vDom 是 React 的一大创举,一句话概括来说就是使用 js 的对象来构建一个,结构树和 dom 的结构一样的对象,一个 div 的 vDom 样子长:

<div></div> 
     |
     ∨
{
    Symbol(react.element)
    ,key: null
    ,props: {children: " "}
    ,ref: null
    ,type: "div"
    ,_owner: ReactCompositeComponentWrapper {_currentElement: {…}, _rootNodeID: 0, _compositeType: 0, _instance: MyTest, _hostParent: ReactDOMComponent, …}
    ,_store: {validated: false}
    ,_self: null
    ,_source: null
    ,__proto__: Object
}

我慢把那些干扰变量拿掉,重要的东西有

<div></div> 
     |
     ∨
{
    props: {children: ''}
    ,type: 'div'
}

是不是已经理解了 vDom 了?我们在来写有子元素的,你就更明白了

<div>
 <h1>我是子元素</h1>
</div> 
   |
   ∨
{
    props: {children: {
        props: {children: “我是子元素”}
        ,type: 'h1'
    }}
    ,type: 'div'
}

加个 props

<div>
 <h1 id="id1">我是子元素</h1>
</div> 
   |
   ∨
{
    props: {children: {
        props: {children: “我是子元素”, id: 'id1'}
        ,type: 'h1'
    }}
    ,type: 'div'
}

好了,vDom 是什么我们已经知道了,那么 react 从哪儿创建这些 dom 的呢?其实也就是你写的 React 组件里 render 的返回值,就是这些 vDom

export default class MyTest extends Component {

  render () {
    const vDom = <div>
      <h1>我是子元素</h1>
    </div>
    console.log( vDom ) // 去控制台上看,就可以看到
    return vDom
  }
}

这里使用了 jsx 语法所以不清晰,翻译成原生 js 就是:

<div>
  <h1>我是子元素</h1>
</div>
   |
   ∨
React.createElement(
    'div',
    null,
    React.createElement(
      'h1',
      null,
      '我是子元素'
    )
);

React.createElement 接受了三个参数进行创建,vDom 至于具体实现,我们这边就不接受了不是今天的话题

有人可能要急了,讲了半天怎么还没讲 React 组件的实例和 vDom 直接的关系,但是其实你看完上面的这些解释就已经大概明白了,只不过差一点火候。

我们都知道,我们在写 React 组件的时候:

当你入口 render 被执行,也就是下面代码

render(<Father />, document.getElementById("root"))

首先会去 new 一个 Father 这个组件,也就是你写的

class Father extends Component {

  componentDidMount () {
    console.log( this.refs['child-h1'] ) // => undefined
  }

  render () {
    
    return <div>
      <ChildComponent ></ChildComponent>
    </div>
  }
}

这就是 Father 组件的实例,然后再会调用此实例的方法 render 进行 vDom 的创建。

所以当你在在一个组件上使用 ref 的时候,我们可以拿到的就是它的实例,也就是:

class Father extends Component {

  componentDidMount () {
    console.log( this.refs['childC'] ) // 兄弟,我就是实例
  }

  render () {
    
    return <div>
      <ChildComponent ref='childC'></ChildComponent>
    </div>
  }
}

class ChildComponent extends Component {
  componentDidMount () {
    console.log( this ) // 兄弟,我就是实例哦 我等于我父组件的 this.refs['childC']
  }
  render () {
    return <div>
      我是子组件
    </div>
  }
}

ref 的回调写法,直接拿到实例

class Father extends Component {

  render () {
    
    return <div>
      <ChildComponent ref={ childItem => {
        console.log( childItem ) // 我是实例哦
      }}></ChildComponent>
    </div>
  }
}

class ChildComponent extends Component {
  componentDidMount () {
    console.log( this ) // 兄弟,我就是实例哦
  }
  render () {
    return <div>
      我是子组件
    </div>
  }
}

如果觉得有帮助,请帮忙点下赞哦,非常感谢

我的:

上一篇下一篇

猜你喜欢

热点阅读