目录
ref(react为了让我们更加方便的获取dom管理dom提出的)
为什么自定义组件要大写字母开头
第一个原因是为了从字面上区分原生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
- 传入三个参数,type(区分自定义dom和原生dom),props,children(第三位包括第三位以后的参数全是children);
- createElement最终会返回一个react element对象,里面有 ¥¥typeof(区分element的类型),type,key,props,ref,_ self,_ source,_ owner这些属性;其中¥¥typeof 可以区分element的类型,例如REACT_ELEMENT_TYPE,REACT_FRAGMENT_TYPE,REACT_STRICT_MODE_TYPE等等;
- 传入的props会根据是否是内建属性来分别处理(内建属性包括ref,key,_ self、_ source),如果不是内建属性就放在props里面,否则就直接放在element对象中;
- 传入的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。