react组件类api

2023-3-28 953 3/28

react中组件类的API,其实在开发中大致用到的只有memo与fwordRef,当然还有其他的,例如PureComponent,lazy这些。

memo:可以用作性能优化,React.memo 是高阶组件,函数组件和类组件都可以使用, 是通过对比组件的props来决定组件是否重新渲染的 其实在开发中,要想去避免子组件的重复渲染,一般都会使用memo加useMemo。因为在react中父组件重新的去渲染了,那么他的子组件也会去重新渲染,一般这是可以使用useMemo,memo这些做一下优化,因为在某些情况下我们不需要子组件重新渲染,而子组件又比较庞大

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

因此我们可以自己去定义对比的粒度

举个栗子 在实际开发中 一般都会与useMemo一起使用,因为如果你props中存在引用类型的数据,那么即使你的props没有发生改变,但是引用类型的数据发生了改变,那么也会导致子组件的重新渲染,这个时候就需要用到useMemo。

 

我的父组件

import { useEffect, useState,useMemo, useCallback} from "react"
import Children from "./components/com/Children"
  export default () => {
    const data = {
        name: '张三'
    }//传入子组件的数据
    const [number, setNumber] = useState(0)
    const addNum =()=>setNumber((state: number) => ++state)
    return <>
        state{ number }
        <button onClick={()=>addNum()}>点击加一</button>
        <Children number={1}  />
    </>
}

子组件

import { useEffect, useState,useMemo, useCallback} from "react"
 type PropsType = {
     number: number,
     data?: { name: string },
     setNumber?: () => void
 }

 export default memo(({ number, data, setNumber }: PropsType) => {
     console.log('子组件渲染了')
     return <>
         接收的父组件的数据
         number {number},
         obj{data?.name}
     </>
 })

react组件类api

这种情况的话是不能组织我的子组件重新渲染的,因为传入的data,setNumber是引用类型,父组件的状态更新后,data,setNumber的内存会被重新分配,还是会重新渲染

那么我们去对data与setNumber做一下缓存(其实这会涉及到fiber,后续会说明useMemo,useCallback是怎么去做的缓存)


import { useEffect, Fragment, lazy, useState,useMemo, useCallback} from "react"
import Children from "./components/com/Children"
 export default () => {
     const data = {
         name: '张三'
     }//传入子组件的数据
     const [number, setNumber] = useState(0)
     //这里我去改变number是直接在Set里面通过回调函数去取的最新的number值,这样就不会出现闭包问题
     //不然的话是需要useCallback的第二个参数取添加对应的依赖项的
     const addNum =useCallback(()=>setNumber((state: number) => ++state),[])
     const memoData=useMemo(()=>data,[])
     return <>
         state{ number }
         <button onClick={()=>addNum()}>点击加一</button>
         <Children number={1} setNumber={addNum} data={memoData}/>
     </>
 }

//重写下父组件,用useMemo,useCallback去做下缓存,但是这里需要注意下依赖的问题。

react组件类api可以看到我的number已经变了,但是子组件已经没有重复的渲染了

 

memo的第二个参数是一个boolean值,我们可以通过回调函数去拿到上次本本次传入的props的值,然后根据我们自己的逻辑去返回true或false,自己决定对比的粒度

  import { memo } from "react"

type PropsType = {
     number: number,
     data?: { name: string },
     setNumber?: () => void
 }
 //memo的第二个参数

 export default memo(({ number, data}: PropsType) => {
     console.log('子组件渲染了')
     return <>
         接收的父组件的数据
         number {number},
         obj{data?.name}
     </>
 },(pre,next)=>{
   //自定义对比
   //如果num小于5渲染,大于5不渲染
   if(next.number>5)
     return true
     return false
 })

这里我们通过回调函数自定义了逻辑,只有props传入的props中的number>5就去重新渲染,否则不渲染

react组件类api 其实可以看到我们父组件的state已经从0累加到了11,但子组件只重复渲染了5次

其实memo在项目中大致就是这样去使用,当然我们正常开发中可能使用的比较少,不过去看一些组件库的原码时或者我们自己去封装组件时是可以用到的

 

 

forwardRef 用于转发ref,可以将ref自动的转发到内部的组件上,这样就可以在父组件中获取到子组件的ref,

其实现在我们主要使用的是函数组件,在函数组件中一般都会与useImperativeHandle一起使用,

因为函数组件中没有实例,所以我们需要通过useImperativeHandle去暴露一些方法给父组件调用,这个时候就需要用到forwardRef

其实这种的话是在一些表单场景用的比较多,或者自己去封装组件,后续结合useImperativeHandle给出使用的例子,不过这个api让我想到了vue3中的defineExpose这个,这个的话有一些与useImperativeHandle不同,因为我们在react中是可以只有forwardRef 透传多层的

 

Fragment或<></> 虚拟元素

react不允许一个组件返回多个节点元素,所以一般我们在开发中都会外层包一下div或者<></>

其实Fragment是与<> </>一样的,只不过Fragment是一个组件,而<> </>是一个语法糖,所以在使用的时候,如果你需要使用key的话,就需要使用Fragment,如果不需要使用key的话,就可以使用语法糖

 

 

 

- THE END -
0

非特殊说明,本博所有文章均为博主原创。

共有 0 条评论