「React进阶」 React全部api解读+基础实践大全(夯实基础2万字总结) (组件类)

简介: React 所有 api 使用大全,是一份不错的学习手册

很多同学用react开发的时候,真正用到的Reactapi少之又少,基本停留在Component,React.memo等层面,实际react源码中,暴露出来的方法并不少,只是我们平时很少用。但是React暴露出这么多api并非没有用,想要玩转react,就要明白这些API究竟是干什么的,应用场景是什么,今天就让我们从reactreact-dom,一次性把react生产环境的暴露api复习个遍(涵盖90%+)。

我们把react,API,分为组件类工具类hooks,再加上 react-dom ,一共四大方向,分别加以探讨。

为了能让屏幕前的你,更理解api,我是绞尽脑汁,本文的每一个api基本都会出一个demo演示效果,弥补一下天书般的react文档😂😂😂,还有就是我对api基本概念的理解。创作不易,希望屏幕前的你能给笔者赏个,以此鼓励我继续创作前端硬文。

老规矩,我们带着疑问开始今天的阅读(自测掌握程度)?

  • 1 react暴露的api有哪些,该如何使用?
  • 2 react提供了哪些自测性能的手段?
  • 3 ref既然不能用在函数组件中,那么父组件如何控制函数子组件内的state和方法?
  • 4 createElementcloneElement有什么区别,应用场景是什么?
  • 5 react内置的children遍历方法,和数组方法,有什么区别?
  • 6 react怎么将子元素渲染到父元素之外的指定容器中?
  • ...

我相信读完这篇文章,这些问题全都会迎刃而解?

组件类,详细分的话有三种类,第一类说白了就是我平时用于继承的基类组件Component,PureComponent,还有就是react提供的内置的组件,比如Fragment,StrictMode,另一部分就是高阶组件forwardRef,memo等。

comp.jpg

Component

Componentclass组件的根基。类组件一切始于Component。对于React.Component使用,我们没有什么好讲的。我们这里重点研究一下reactComponent做了些什么。

react/src/ReactBaseClasses.js

function Component(props, context, updater) {
   
   
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

这就是Component函数,其中updater对象上保存着更新组件的方法。

我们声明的类组件是什么时候以何种形式被实例化的呢?

react-reconciler/src/ReactFiberClassComponent.js

constructClassInstance

function constructClassInstance(
    workInProgress,
    ctor,
    props
){
   
   
   const instance = new ctor(props, context);
    instance.updater = {
   
   
        isMounted,
        enqueueSetState(){
   
   
            /* setState 触发这里面的逻辑 */
        },
        enqueueReplaceState(){
   
   },
        enqueueForceUpdate(){
   
   
            /* forceUpdate 触发这里的逻辑 */
        }
    }
}

对于Componentreact 处理逻辑还是很简单的,实例化我们类组件,然后赋值updater对象,负责组件的更新。然后在组件各个阶段,执行类组件的render函数,和对应的生命周期函数就可以了。

PureComponent

PureComponentComponent用法,差不多一样,唯一不同的是,纯组件PureComponent会浅比较,propsstate是否相同,来决定是否重新渲染组件。所以一般用于性能调优,减少render次数。

什么叫做浅比较,我这里举个列子:

class Index extends React.PureComponent{
   
   
    constructor(props){
   
   
        super(props)
        this.state={
   
   
           data:{
   
   
              name:'alien',
              age:28
           }
        }
    }
    handerClick= () =>{
   
   
        const {
   
    data } = this.state
        data.age++
        this.setState({
   
    data })
    }
    render(){
   
   
        const {
   
    data } = this.state
        return <div className="box" >
        <div className="show" >
            <div> 你的姓名是: {
   
    data.name } </div>
            <div> 年龄: {
   
    data.age  }</div>
            <button onClick={
   
    this.handerClick } >age++</button>
        </div>
    </div>
    }
}

pureComponent.gif
点击按钮,没有任何反应,因为PureComponent会比较两次data对象,都指向同一个data,没有发生改变,所以不更新视图。

解决这个问题很简单,只需要在handerClick事件中这么写:

 this.setState({
   
    data:{
   
   ...data} })

浅拷贝就能根本解决问题。

memo

React.memoPureComponent作用类似,可以用作性能优化,React.memo 是高阶组件,函数组件和类组件都可以使用, 和区别PureComponentReact.memo只能对props的情况确定是否渲染,而PureComponent是针对propsstate

React.memo 接受两个参数,第一个参数原始组件本身,第二个参数,可以根据一次更新中props是否相同决定原始组件是否重新渲染。是一个返回布尔值,true 证明组件无须重新渲染,false证明组件需要重新渲染,这个和类组件中的shouldComponentUpdate()正好相反 。

React.memo: 第二个参数 返回 true 组件不渲染 , 返回 false 组件重新渲染。
shouldComponentUpdate: 返回 true 组件渲染 , 返回 false 组件不渲染。

接下来我们做一个场景,控制组件在仅此一个props数字变量,一定范围渲染。

例子🌰:

控制 props 中的 number

  • 1 只有 number 更改,组件渲染。

  • 2 只有 number 小于 5 ,组件渲染。

function TextMemo(props){
   
   
    console.log('子组件渲染')
    if(props)
    return <div>hello,world</div> 
}

const controlIsRender = (pre,next)=>{
   
   
   if(pre.number === next.number  ){
   
    // number 不改变 ,不渲染组件
       return true 
   }else if(pre.number !== next.number && next.number > 5 ) {
   
    // number 改变 ,但值大于5 , 不渲染组件
       return true
   }else {
   
    // 否则渲染组件
       return false
   }
}

const NewTexMemo = memo(TextMemo,controlIsRender)
class Index extends React.Component{
   
   
    constructor(props){
   
   
        super(props)
        this.state={
   
   
            number:1,
            num:1
        }
    }
    render(){
   
   
        const {
   
    num , number }  = this.state
        return <div>
            <div>
                改变num:当前值 {
   
    num }  
                <button onClick={
   
    ()=>this.setState({
   
    num:num + 1 }) } >num++</button>
                <button onClick={
   
    ()=>this.setState({
   
    num:num - 1 }) } >num--</button>  
            </div>
            <div>
                改变number: 当前值 {
   
    number } 
                <button onClick={
   
    ()=>this.setState({
   
    number:number + 1 }) } > number ++</button>
                <button onClick={
   
    ()=>this.setState({
   
    number:number - 1 }) } > number -- </button>  
            </div>
            <NewTexMemo num={
   
    num } number={
   
   number}  />
        </div>
    }
}

效果:

memo.gif

完美达到了效果,React.memo一定程度上,可以等价于组件外部使用shouldComponentUpdate ,用于拦截新老props,确定组件是否更新。

forwardRef

官网对forwardRef的概念和用法很笼统,也没有给定一个具体的案例。很多同学不知道 forwardRef具体怎么用,下面我结合具体例子给大家讲解forwardRef应用场景。

1 转发引入Ref

这个场景实际很简单,比如父组件想获取孙组件,某一个dom元素。这种隔代ref获取引用,就需要forwardRef来助力。

function Son (props){
   
   
    const {
   
    grandRef } = props
    return <div>
        <div> i am alien </div>
        <span ref={
   
   grandRef} >这个是想要获取元素</span>
    </div>
}

class Father extends React.Component{
   
   
    constructor(props){
   
   
        super(props)
    }
    render(){
   
   
        return <div>
            <Son grandRef={
   
   this.props.grandRef}  />
        </div>
    }
}

const NewFather = React.forwardRef((props,ref)=><Father grandRef={
   
   ref}  {
   
   ...props} />  )

class GrandFather extends React.Component{
   
   
    constructor(props){
   
   
        super(props)
    }
    node = null 
    componentDidMount(){
   
   
        console.log(this.node)
    }
    render(){
   
   
        return <div>
            <NewFather ref={
   
   (node)=> this.node = node } />
        </div>
    }
}

效果

forwaedRef.jpg

react不允许ref通过props传递,因为组件上已经有 ref 这个属性,在组件调和过程中,已经被特殊处理,forwardRef出现就是解决这个问题,把ref转发到自定义的forwardRef定义的属性上,让ref,可以通过props传递。

2 高阶组件转发Ref

一文吃透hoc文章中讲到,由于属性代理的hoc,被包裹一层,所以如果是类组件,是通过ref拿不到原始组件的实例的,不过我们可以通过forWardRef转发ref

function HOC(Component){
   
   
  class Wrap extends React.Component{
   
   
     render(){
   
   
        const {
   
    forwardedRef ,...otherprops  } = this.props
        return <Component ref={
   
   forwardedRef}  {
   
   ...otherprops}  />
     }
  }
  return  React.forwardRef((props,ref)=> <Wrap forwardedRef={
   
   ref} {
   
   ...props} /> ) 
}
class Index extends React.Component{
   
   
  componentDidMount(){
   
   
      console.log(666)
  }
  render(){
   
   
    return <div>hello,world</div>
  }
}
const HocIndex =  HOC(Index,true)
export default ()=>{
   
   
  const node = useRef(null)
  useEffect(()=>{
   
   
     /* 就可以跨层级,捕获到 Index 组件的实例了 */ 
    console.log(node.current.componentDidMount)
  },[])
  return <div><HocIndex ref={
   
   node}  /></div>
}

如上,解决了高阶组件引入Ref的问题。

lazy

React.lazy 和 Suspense 技术还不支持服务端渲染。如果你想要在使用服务端渲染的应用中使用,我们推荐 Loadable Components 这个库

React.lazySuspense配合一起用,能够有动态加载组件的效果。React.lazy 接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise ,该 Promise 需要 resolve 一个 default exportReact 组件。

我们模拟一个动态加载的场景。

父组件

import Test from './comTest'
const LazyComponent =  React.lazy(()=> new Promise((resolve)=>{
   
   
      setTimeout(()=>{
   
   
          resolve({
   
   
              default: ()=> <Test />
          })
      },2000)
}))
class index extends React.Component{
   
      
    render(){
   
   
        return <div className="context_box"  style={
   
    {
   
    marginTop :'50px' } }   >
           <React.Suspense fallback={
   
    <div className="icon" ><SyncOutlined  spin  /></div> } >
               <LazyComponent />
           </React.Suspense>
        </div>
    }
}

我们用setTimeout来模拟import异步引入效果。

Test

class Test extends React.Component{
   
   
    constructor(props){
   
   
        super(props)
    }
    componentDidMount(){
   
   
        console.log('--componentDidMount--')
    }
    render(){
   
   
        return <div>
            <img src={
   
   alien}  className="alien" />
        </div>
    }
}

效果

lazy.gif

Suspense

何为Suspense, Suspense 让组件“等待”某个异步操作,直到该异步操作结束即可渲染。

用于数据获取的 Suspense 是一个新特性,你可以使用 <Suspense> 以声明的方式来“等待”任何内容,包括数据。本文重点介绍它在数据获取的用例,它也可以用于等待图像、脚本或其他异步的操作。

上面讲到高阶组件lazy时候,已经用 lazy + Suspense模式,构建了异步渲染组件。我们看一下官网文档中的案例:

const ProfilePage = React.lazy(() => import('./ProfilePage')); // 懒加载
<Suspense fallback={
   
   <Spinner />}>
  <ProfilePage />
</Suspense>

Fragment

react不允许一个组件返回多个节点元素,比如说如下情况

render(){
   
   
    return <li> 🍎🍎🍎 </li>
           <li> 🍌🍌🍌 </li>
           <li> 🍇🍇🍇 </li>
}

如果我们想解决这个情况,很简单,只需要在外层套一个容器元素。

render(){
   
   
    return <div>
           <li> 🍎🍎🍎 </li>
           <li> 🍌🍌🍌 </li>
           <li> 🍇🍇🍇 </li>
    </div>
}

但是我们不期望,增加额外的dom节点,所以react提供Fragment碎片概念,能够让一个组件返回多个元素。
所以我们可以这么写

<React.Fragment>
    <li> 🍎🍎🍎 </li>
    <li> 🍌🍌🍌 </li>
    <li> 🍇🍇🍇 </li>
</React.Fragment>

还可以简写成:

<>
    <li> 🍎🍎🍎 </li>
    <li> 🍌🍌🍌 </li>
    <li> 🍇🍇🍇 </li>
</>

Fragment区别是,Fragment可以支持key属性。<></>不支持key属性。

温馨提示。我们通过map遍历后的元素,react底层会处理,默认在外部嵌套一个<Fragment>

比如:

{
   
   
   [1,2,3].map(item=><span key={
   
   item.id} >{
   
    item.name }</span>)
}

react底层处理之后,等价于:

<Fragment>
   <span></span>
   <span></span>
   <span></span>
</Fragment>

Profiler

Profiler这个api一般用于开发阶段,性能检测,检测一次react组件渲染用时,性能开销。

Profiler 需要两个参数:

第一个参数:是 id,用于表识唯一性的Profiler

第二个参数:onRender回调函数,用于渲染完成,接受渲染参数。

实践:

const index = () => {
   
   
  const callback = (...arg) => console.log(arg)
  return <div >
    <div >
      <Profiler id="root" onRender={
   
    callback }  >
        <Router  >
          <Meuns/>
          <KeepaliveRouterSwitch withoutRoute >
              {
   
    renderRoutes(menusList) }
          </KeepaliveRouterSwitch>
        </Router>
      </Profiler> 
    </div>
  </div>
}

结果

Profiler.jpg

onRender

  • 0 -id: root -> Profiler 树的 id
  • 1 -phase: mount -> mount 挂载 , update 渲染了。
  • 2 -actualDuration: 6.685000262223184 -> 更新 committed 花费的渲染时间。
  • 3 -baseDuration: 4.430000321008265 -> 渲染整颗子树需要的时间
  • 4 -startTime : 689.7299999836832 -> 本次更新开始渲染的时间
  • 5 -commitTime : 698.5799999674782 -> 本次更新committed 的时间
  • 6 -interactions: set{} -> 本次更新的 interactions 的集合

尽管 Profiler 是一个轻量级组件,我们依然应该在需要时才去使用它。对一个应用来说,每添加一些都会给 CPU 和内存带来一些负担。

StrictMode

StrictMode见名知意,严格模式,用于检测react项目中的潜在的问题,。与 Fragment 一样, StrictMode 不会渲染任何可见的 UI 。它为其后代元素触发额外的检查和警告。

严格模式检查仅在开发模式下运行;它们不会影响生产构建。

StrictMode目前有助于:

  • ①识别不安全的生命周期。
  • ②关于使用过时字符串 ref API 的警告
  • ③关于使用废弃的 findDOMNode 方法的警告
  • ④检测意外的副作用
  • ⑤检测过时的 context API

实践:识别不安全的生命周期

对于不安全的生命周期,指的是UNSAFE_componentWillMountUNSAFE_componentWillReceiveProps , UNSAFE_componentWillUpdate

外层开启严格模式:

<React.StrictMode> 
    <Router  >
        <Meuns/>
        <KeepaliveRouterSwitch withoutRoute >
            {
   
    renderRoutes(menusList) }
        </KeepaliveRouterSwitch>
    </Router>
</React.StrictMode>

我们在内层组件中,使用不安全的生命周期:

class Index extends React.Component{
   
       
    UNSAFE_componentWillReceiveProps(){
   
   
    }
    render(){
   
         
        return <div className="box" />   
    }
}

效果:

strictMode.jpg

相关文章
|
21天前
|
存储 API 计算机视觉
自学记录HarmonyOS Next Image API 13:图像处理与传输的开发实践
在完成数字版权管理(DRM)项目后,我决定挑战HarmonyOS Next的图像处理功能,学习Image API和SendableImage API。这两个API支持图像加载、编辑、存储及跨设备发送共享。我计划开发一个简单的图像编辑与发送工具,实现图像裁剪、缩放及跨设备共享功能。通过研究,我深刻体会到HarmonyOS的强大设计,未来这些功能可应用于照片编辑、媒体共享等场景。如果你对图像处理感兴趣,不妨一起探索更多高级特性,共同进步。
73 11
|
17天前
|
人工智能 数据可视化 API
自学记录鸿蒙API 13:Calendar Kit日历功能从学习到实践
本文介绍了使用HarmonyOS的Calendar Kit开发日程管理应用的过程。通过API 13版本,不仅实现了创建、查询、更新和删除日程等基础功能,还深入探索了权限请求、日历配置、事件添加及查询筛选等功能。实战项目中,开发了一个智能日程管理工具,具备可视化管理、模糊查询和智能提醒等特性。最终,作者总结了模块化开发的优势,并展望了未来加入语音助手和AI推荐功能的计划。
132 1
|
2月前
|
XML JSON 缓存
深入理解RESTful API设计原则与实践
在现代软件开发中,构建高效、可扩展的应用程序接口(API)是至关重要的。本文旨在探讨RESTful API的核心设计理念,包括其基于HTTP协议的特性,以及如何在实际应用中遵循这些原则来优化API设计。我们将通过具体示例和最佳实践,展示如何创建易于理解、维护且性能优良的RESTful服务,从而提升前后端分离架构下的开发效率和用户体验。
|
1月前
|
机器学习/深度学习 搜索推荐 API
淘宝/天猫按图搜索(拍立淘)API的深度解析与应用实践
在数字化时代,电商行业迅速发展,个性化、便捷性和高效性成为消费者新需求。淘宝/天猫推出的拍立淘API,利用图像识别技术,提供精准的购物搜索体验。本文深入探讨其原理、优势、应用场景及实现方法,助力电商技术和用户体验提升。
|
2月前
|
JavaScript 前端开发 API
Vue.js 3:深入探索组合式API的实践与应用
Vue.js 3:深入探索组合式API的实践与应用
|
2月前
|
前端开发 JavaScript API
探究 React Hooks:如何利用全新 API 优化组件逻辑复用与状态管理
本文深入探讨React Hooks的使用方法,通过全新API优化组件逻辑复用和状态管理,提升开发效率和代码可维护性。
|
2月前
|
缓存 API 开发者
构建高效后端服务:RESTful API设计原则与实践
【10月更文挑战第43天】在数字化时代的浪潮中,后端服务的稳定性和效率成为企业竞争力的关键。本文将深入探讨如何构建高效的后端服务,重点介绍RESTful API的设计原则和实践技巧,帮助开发者提升服务的可用性、可扩展性和安全性。通过实际代码示例,我们将展示如何将这些原则应用到日常开发工作中,以确保后端服务能够支撑起现代Web和移动应用的需求。
|
2月前
|
存储 JSON 测试技术
构建高效后端API:实践和原则
【10月更文挑战第43天】本文深入探讨了如何设计和实现高效、可维护的后端API,强调了设计哲学、最佳实践和常见陷阱。通过具体示例,我们展示了如何运用这些原则来提高API的性能和可用性。
|
2月前
|
Prometheus 监控 Java
深入探索:自制Agent监控API接口耗时实践
在微服务架构中,监控API接口的调用耗时对于性能优化至关重要。通过监控接口耗时,我们可以识别性能瓶颈,优化服务响应速度。本文将分享如何自己动手实现一个Agent来统计API接口的调用耗时,提供一种实用的技术解决方案。
74 3
|
1月前
|
监控 搜索推荐 测试技术
电商API的测试与用途:深度解析与实践
在电子商务蓬勃发展的今天,电商API成为连接电商平台、商家、消费者和第三方开发者的重要桥梁。本文深入探讨了电商API的核心功能,包括订单管理、商品管理、用户管理、支付管理和物流管理,并介绍了有效的测试技巧,如理解API文档、设计测试用例、搭建测试环境、自动化测试、压力测试、安全性测试等。文章还详细阐述了电商API的多样化用途,如商品信息获取、订单管理自动化、用户数据管理、库存同步、物流跟踪、支付处理、促销活动管理、评价管理、数据报告和分析、扩展平台功能及跨境电商等,旨在为开发者和电商平台提供有益的参考。
59 0