重生之我又来学React了Day06 -- React Api reference

96 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

image.png

什么是React Api reference?就是React顶层Api。我们都知道jsx其实就是React.createElment的语法糖,React.createElement就属于React顶层Api。

React.Component()

React的类组件都继承自React.Compoent,比如类组件的render()方法,setState()方法,生命周期等都是在React.Component这个父类中实现的。

import { Component } from 'react'
export class index extends Component {
  static propTypes = {
    second: third
  }

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

React.PureComponent()

从命名上我们可以看出这个方法是创建一个纯组件,我们知道在类组件中使用shouldComponentUpdate()方法可以控制组件是否更新渲染。然而PureComponent自己就“帮”我们使用了该方法,当props或者state浅对比没有更改的时候,就不会渲染更新视图。

import { Component, PureComponent } from 'react'

export default class index extends Component {
  constructor() {
    super()
  }
  render() {
    return (
      <div>
        <Pure></Pure>
        <Com></Com>
      </div>
    )
  }
}

class Pure extends PureComponent {
  constructor() {
    super()
    this.state = {
      name: 'pure'
    }
  }
  changeHandler = () => {
    this.setState({
      name: 'pure'
    })
  }
  componentDidUpdate() {
    console.log('pure componentDidUpdate')
  }
  render() {
    console.log('render pure')
    return (
      <div>
        <p>我是纯组件{this.state.name}</p>
        {/* 点击按钮不会触发componentDidUpdate 和render 因为state并没有修改 */}
        <button onClick={() => this.changeHandler()}>修改state</button>
      </div>
    )
  }
}

class Com extends Component {
  constructor() {
    super()
    this.state = {
      name: 'component'
    }
  }
  componentDidUpdate() {
    console.log('com componentDidUpdate')
  }
  changeHandler = () => {
    this.setState({
      name: 'component'
    })
  }
  render() {
    console.log('render com')
    return (
      <div>
        <p>我是一般组件{this.state.name}</p>
        {/* 点击按钮会触发componentDidUpdate 和render 即使state并没有修改 */}
        <button onClick={() => this.changeHandler()}>修改state</button>
      </div>
    )
  }
}

React.memo()

这是一个高阶组件,但是它用于在函数组件上。当你的组件接受相同的props时,不需要更新渲染,就可以使用React.memo把这个组件包裹起来。React.memo仅检查props变更。

import {useState, memo} from 'react';
export default function App() {
  const [first, setfirst] = useState('first')
  return (
    <div>
        {/* 点击按钮并不会触发MemoChild1再次渲染,因为props并未修改 */} 
      <button onClick={() => setfirst('haha')}>改变父亲</button>
      <MemoChild1></MemoChild1>
      <Child2></Child2>
    </div>
  )
}

const MemoChild1 = memo(function Child1() {
  console.log('child1')
  return (
    <div>123</div>
  )
})

function Child2() {
  console.log('child2')
  return (
    <div>321</div>
  )
}

React.memo还可以传入第二个参数,如果你想要控制对比过程,那么可以将自定义的比较函数通过第二个参数传入来实现。

const MemoChild1 = memo(function Child1() {
  console.log('child1')
  return (
    <div>123</div>
  )
}, (prevProps, nextProps) => {
 // 如果之前传入的props.first值为first 则不“记忆”该组件,返回true则“记忆”该组件
  if(prevProps.first === 'first') {
    return false
  } else {
    return true
  }
})

React.createElement()

React.createElement(
  type,
  [props],
  [...children]
)

创建并返回指定类型的新React元素,类型参数既可以是字符串也可以是React组件。JSX则是该方法的语法糖。

React.cloneElement()

React.cloneElement(
  element,
  [config],
  [...children]
)

以element元素为样板克隆并返回新的 React 元素。config中可包含新的props,key或ref。得到的元素的props是样板组件的props和config里面的props的浅合并结果。新元素的子元素取代样板元素的子元素。

React.isValidElement()

用于判断一个对象是否是React元素,返回true或false。

React.Children

React.Children为操作this.children.props这个数据结构提供了一些工具方法。

React.children.map(this.props.children, (item) => {
    return <div>{item}</div>
})

React.Fragment

创建react组件的话需要一个根元素,但是根元素其实是多余的,所以我们可以使用<React.Fragment></React.Fragment>来充当这个根元素,并且它不会渲染出DOM元素,其简单写法为<></>

React.createRef()

创建一个可通过ref属性附加到React元素上的ref

import React from 'react'
export default class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.divRef = React.createRef();  
    this.ComRef = React.createRef();
  }

  render() {
    return (
      <>
        <div ref={this.divRef}></div>
        <List ref={this.ComRef}></List>
      </>
    )
  }

  componentDidMount() {
    // 对该节点的引用在ref的current属性中
    // 当原生HTML使用ref属性的时候,React.createRef()创建的ref对象的current属性就是所对应的原生DOM元素
    console.log('原生dom节点:', this.divRef.current);  
    // 当react组件使用ref属性的时候,React.createRef()创建的ref对象的current属性就是组件的挂载实例
    console.log('react组件:', this.ComRef.current); 
   }
}

class List extends React.Component {
 render() {
  return (
    <div>list</div>
  )
 }
}

React.forwardRef()

如果在函数组件上面使用React.createRef(),这个时候控制台会报错,因为默认情况下,函数组件是没有实例的,就需要使用React.forwardRef()(可与 useImperativeHandle 结合使用)转发ref。

在应用组件中来说ref转发不常用,但是在可重用的组件库中这是很有用的。(ref不像props作为参数可以传递,所以如果要想传递ref就得用forwardRef)

React.forwardRef()的返回值是react组件,接受一个render函数。

import React from 'react'
export default class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.divRef = React.createRef();  
    this.ComRef = React.createRef();
  }

  render() {
    return (
      <>
        <div ref={this.divRef}></div>
        <FancyDiv ref={this.ComRef}></FancyDiv>
      </>
    )
  }

  componentDidMount() {
    console.log('拿到传进去的ref:', this.ComRef.current); 
   }
}

// 让函数组件可以拥有ref属性
const FancyDiv = React.forwardRef((props, ref) => (
  <div>
    <span>123</span>
    <p ref={ref}>321</p>
  </div>
));

image.png

结合useImperativeHandle使用,父组件可使用子组件的方法

import React, { useImperativeHandle } from 'react'
export default class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.divRef = React.createRef();  
    this.ComRef = React.createRef();
  }
  clickHandler = () => {
    this.ComRef.current.logSon();
  }
  render() {
    return (
      <>
        <div ref={this.divRef}></div>
        <FancyDiv ref={this.ComRef}></FancyDiv>
        <button onClick={() => this.clickHandler()}>触发子组件的方法</button>
      </>
    )
  }

  componentDidMount() {
    console.log('拿到传进去的ref:', this.ComRef.current); 
   }
}

const FancyDiv = React.forwardRef((props, ref) => {
  // ref表示需要转发的ref,第二个参数返回的对象就是要传给父组件的属性和方法,第三个参数是依赖项,如果函数中有使用变量,则都需要写到第三个参数中,如果没有则可忽略
  useImperativeHandle(ref, () => {
    return {
      logSon: () => {
        console.log('测试');
      }
    }
  },[])
  return (
      <div>
        <span>123</span>
        <p ref={ref}>321</p>
      </div>
    )
});

image.png

React.lazy

用于动态加载组件,有助于缩减bundle的体积,该组件的使用依赖于React.Suspense组件。

// index.js
import { useState } from 'react';
import Tab from '../../components/Tab'
export default function App() {
  const [first, setfirst] = useState(false)
  return (
    <div>
      {first && <Tab></Tab>}
      <button onClick={() => setfirst(true)}>按钮1</button>
    </div>
  )
}

// tab.js
export default () => {
  return(
    <div>wo shi tab</div>
  )
}

当我们直接引入组件,页面加载完毕,此时页面还不满足出现tab组件的条件,但是bundle里面已经有tab组件的代码了

image.png

我们再来看看使用React.lazy的效果

import {useState, Suspense, lazy} from 'react';
const Tab = lazy(() => import('../../components/Tab'));
export default function App() {
  const [first, setfirst] = useState(false)
  return (
    <div>
    {/* 当点击按钮的时候 才会加载tab组件的代码 */}
    <button onClick={() => setfirst(true)}>按钮1</button>
      <Suspense>
      <div>
        {first && <Tab />}
      </div>
    </Suspense>
    </div>
  )
}

image.png

我们可以看到这个时候bundle里面并没有tab组件的代码,然后我们点击按钮,让tab组件出现

image.png 这个时候会发现引入了一个src_components_Tab_index_js.chunk.js的js,这个js的内容就是tab组件了

React.Suspense

React.Suspense可以指定加载指示器。

// <Spinner />组件为一个loading组件 <Spinner />组件会一直显示直到<Tab>组件加载完毕
<React.Suspense fallback={<Spinner />}>
      <div>
        <Tab />
      </div>
</React.Suspense>