React 源码(一)

目录

为什么自定义组件要大写字母开头

React.createElement

ref(react为了让我们更加方便的获取dom管理dom提出的)

1. 类组件

2. 函数组件 / pureComponent

为什么自定义组件要大写字母开头

第一个原因是为了从字面上区分原生dom和自定义组件;
其次因为 jsx 转换成 js 语法是通过 babel 编译的,babel 会把 jsx 语法编译成 React.createElement('div',{...props},children)的形式,如果是自定义组件,那么React.createElement第一个参数会以变量的形式传入这个组件,如果是小写,就会编译为字符串,字符串就会被认为是原生dom节点,但是没有这个节点,因此最终会报错。

//大写
function Comp(){
    return <div>ssss</div>
}

//另一个组件中
<Comp id='id'>
    <div>111</div>
</Comp>
//babel编译以后
React.createElement(Comp,{id:'id'},React.createElement('div',null.'111'))

//小写
function comp(){
    return <div>ssss</div>
}

//另一个组件中
<comp id='id'>
    <div>111</div>
</comp>
//babel编译以后
React.createElement('comp',{id:'id'},React.createElement('div',null.'111')) //报错

React.createElement

  1. 传入三个参数,type(区分自定义dom和原生dom),props,children(第三位包括第三位以后的参数全是children);
  2. createElement最终会返回一个react element对象,里面有 ¥¥typeof(区分element的类型),type,key,props,ref,_ self,_ source,_ owner这些属性;其中¥¥typeof 可以区分element的类型,例如REACT_ELEMENT_TYPE,REACT_FRAGMENT_TYPE,REACT_STRICT_MODE_TYPE等等;
  3. 传入的props会根据是否是内建属性来分别处理(内建属性包括ref,key,_ self、_ source),如果不是内建属性就放在props里面,否则就直接放在element对象中;
  4. 传入的children放在数组里,然后放在props中;
//打印一下React.createElement创建的element对象
class Class extends React.Component {
  render() {
    return <div>www</div>
  }
}
console.log(React.createElement(Class,{key:'ddd',id:'a'},111));

ref(react为了让我们更加方便的获取dom管理dom提出的)

1. 类组件

需要注意的是,因为函数组件没有实例,所以不能用下面这三种方式获取ref。

① 字符串形式(不建议使用)

直接给组件或者原生dom传递ref属性,获取到组件实例或者dom实例;

import React, { Component } from 'react'

export default class Func extends Component {
  render() {
    return (
      <div>Func</div>
    )
  }
}


//index
import React, { Component } from 'react'

export default class MyCom extends Component {
 componentDidMount() {   
    console.log('refs',this.refs.func);
  }
  render() {
    return (
      <div>
        <Func ref='func' />
      </div>
    )
  }
}

② 回调函数callback

格式:ref={ ele => this.XXX = ele } ,其中ele代表的是组件或者dom实例,意为将实例绑定到this的XXX属性上,通过this.XXX获取实例。

import React, { Component } from 'react';
import Func from './Func';

export default class MyCom extends Component {
 componentDidMount() {  
    console.log('refs',this.func);
  }
  render() {
    return (
      <Func ref={e=>this.func=e}  />
    )
  }
}

 

 ③ React.createRef(react的api)

创建一个包含current属性的对象,之后的阶段会把实例挂在在current属性上,可以通过this.XXX.current获取实例。

//源码
import type {RefObject} from 'shared/ReactTypes';

// an immutable object with a single mutable value
export function createRef(): RefObject {
  const refObject = {
    current: null,
  };
  if (__DEV__) {
    Object.seal(refObject);
  }
  return refObject;
}
//demo
import React, { Component,createRef } from 'react';
import Func from './Func';

export default class MyCom extends Component {
  constructor(props) {
    super(props);
    this.state = { num: 0, people: 2 };
    this.func = createRef();
  }

  componentDidMount() {
    console.log('refs', this.func);
  }

  render() {
    return (
      <Func ref={this.func} />
    )
  }
}

2. 函数组件 / pureComponent

① useRef(生成ref)

函数组件中可以通过这个hook代替类组件中的createRef去绑定DOM,获取DOM实例;

//demo
import React, { useRef, useEffect } from 'react';

export default function Class() {
  let func = useRef(null);

  useEffect(() => {
    console.log('func', func);
    func.current.value='useRef'; //默认绑定在ref对象的current属性上,和createRef一样
  }, [])

  return (
    <div>
      <input type='text' ref={func} />
    </div>
  )
}

② forwardRef(传递ref)

先来看一下源码;

//源码
import {REACT_FORWARD_REF_TYPE} from 'shared/ReactSymbols';

import warningWithoutStack from 'shared/warningWithoutStack';

export default function forwardRef<Props, ElementType: React$ElementType>( 
  render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
//接收一个callback  (props,ref)=> { return ReactElement(porps,ref) }

//.......

  return {
    $$typeof: REACT_FORWARD_REF_TYPE,  //注意这个 $$typeof
    render,
  };
}

我们通过下面的demo来使用并讲解一下部分源码;

//demo
import React from 'react'

/*从源码中可以看出React.forwardRef返回一个对象,它有
一个$$typeof属性是REACT_FORWARD_REF_TYPE*/

//这里相当于 TargetComponent = {$$typeof,render}

const TargetComponent = React.forwardRef((props, ref) => ( 
  <input type="text" ref={ref} />
))

export default class Comp extends React.Component {
  constructor() {
    super()
    this.ref = React.createRef()
  }

  componentDidMount() {
    this.ref.current.value = 'ref get input'
  }

/*前面我们说过jsx语法会被转换成React.createElement的调用,
它返回的是一个带有type属性的对象,函数组件的type是大写字母
开头的变量名,也就是说这里如果不被React.forwardRef包裹,它
的type就是TargetComponent ,但是它被React.forwardRef包裹
了,因此它的type现在是REACT_FORWARD_REF_TYPE
*/

  render() {
    return <TargetComponent ref={this.ref} /> 
  }
}

 forwardRef会返回一个对象,对象中包含 ¥¥typeof和传入的callback渲染的具体的DOM,这里的¥¥typeof其实是这个组件的type。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值