react中不是那么常用的hooks

2023-4-27 1,002 4/27

其实在日常的开发中,对于小型的项目,useState,useEffect,useRef已经足够了,且也无需状态管理方案。

 

看一下不常用的钩子有哪些吧

useMemo,缓存的钩子,useMemo接受两个参数,第一个参数是一个函数,返回值用于产生保存值。 第二个参数是一个数组,作为dep依赖项,数组里面的依赖项发生变化,重新执行第一个函数,产生新的值。一般都是用来缓存一些值,避免重复渲染子组件,或者对一些较为复杂耗时的计算的结果进行缓存,避免状态刷新后在次计算。也会与memo高阶组件组合,避免子组件重复渲染

缓存值

const newNumber = useMemo(()=>{
    /** ....大量的逻辑运算 **/
   return number
},[ props.number ]) // 只有 props.number 改变的时候,重新计算number的值。

 

也可以减少子组件的重复渲染

/* 只有当props中,list列表改变的时候,子组件才渲染 */
const  ListChild = useMemo(()=> <GoodList list={ props.list } /> ,[ props.list ])
useMemouseCallback 接收的参数都是一样,都是在其依赖项发生变化后才执行,都是返回缓存的值,区别在于 useMemo 返回的是函数运行的结果, useCallback 返回的是函数。 返回的callback可以作为props回调函数传递给子组件。
因为内置的hooks都是挂载到的组件对应的fiber节点上,其实useMemo与useCallback正在的区别在于useMemo在fiber中是缓存的值,其实每次依赖项变换后,都会去重新计算一遍,再去替换掉fiber中的值,而useCallback只需去替换掉缓存的函数对象。
同意与memo组合去阻止我的子组件重复渲染
const Children = React.memo((props)=>{
   
   useEffect(()=>{
       props.getInfo('子组件')
   },[])
   return <div>子组件</div>
})

const Demo=({ id })=>{
    const [number, setNumber] = useState(1)
    /* 此时usecallback的第一参数 (sonName)=>{ console.log(sonName) }
     经过处理赋值给 getInfo */
    const getInfo  = useCallback((sonName)=>{
          console.log(sonName)
    },[id])
    return <div>
        {/* 点击按钮触发父组件更新 ,但是子组件没有更新 */}
        <button onClick={ ()=>setNumber(number+1) } >增加</button>
        <Children getInfo={getInfo} />
    </div>
}

 

useLayoutEffect: 组件更新挂载完成 -> 浏览器 dom 绘制完成 -> 执行 useEffect 回调(其实useEffect的处理时在commit刚开始就出处理了)。 useLayoutEffect 执行顺序: 组件更新挂载完成 -> 执行 useLayoutEffect 回调-> 浏览器dom绘制完成。

所以说 useLayoutEffect 代码可能会阻塞浏览器的绘制 。我们写的 effectuseLayoutEffectreact在底层会被分别打上PassiveEffectHookLayout,在commit阶段区分出,在什么时机执行。所以在去debug才会觉得useLayoutEffect先被触发执行,这是不对的,只是因为useLayoutEffect的回调函数的执行是同步的而已 一般useLayoutEffect在项目中是很少用到的,而且react官方也是不太推荐去使用的,除非是由特殊的需求

 

useReducer 接受的第一个参数是一个函数,可以认为它就是一个 reducer , reducer 的参数就是常规 reducer 里面的 stateaction ,返回改变后的 state , useReducer 第二个参数为 state 的初始值 返回一个数组,数组的第一项就是更新之后 state 的值 ,第二个参数是派发更新的 dispatch 函数。
const MyReducer = ()=>{
    /* number为更新后的state值,  dispatchNumbner 为当前的派发函数 */
   const [ number , dispatchNumbner ] = useReducer((state,action)=>{
       const { payload , name  } = action
       /* return的值为新的state */
       switch(name){
           case 'add':
               return state + 1
           case 'sub':
               return state - 1 
           case 'reset':
             return payload       
       }
       return state
   },0)
   return <div>
      当前值:{ number }
      { /* 派发更新 */ }
      <button onClick={()=>dispatchNumbner({ name:'add' })} >增加</button>
      <button onClick={()=>dispatchNumbner({ name:'sub' })} >减少</button>
      <button onClick={()=>dispatchNumbner({ name:'reset' ,payload:666 })} >赋值</button>
      { /* 把dispatch 和 state 传递给子组件  */ }
      <MyChildren  dispatch={ dispatchNumbner } State={{ number }} />
   </div>
}
useContext ,可以用来获取父级组件传递过来的 context 值,这个当前值就是最近的父级组件 Provider 设置的 value 值,useContext 参数一般是由 createContext 方式引入 ,也可以父级上下文 context 传递 ( 参数为 context )。
const MyContext = ()=> {
    const value:any = useContext(Context)
return <div> my name is { value.name }</div>
}

constContext1 = ()=>{
    return <Context.Consumer>
        { (value)=> <div> my name is { value.name }</div> }
    </Context.Consumer>
}

export default ()=>{
    return <div>
        <Context.Provider value={{ name:'alien' , age:18 }} >
            <MyContext />
            <Context1 />
        </Context.Provider>
    </div>
}

 

其实对我个人而言,这两个钩子几乎没用到过,emmm,但自己也有去对应的了解,其实我觉得一般用到这两个钩子的话一般都会使用数据流方案了。
useImperativeHandle最后一个钩子,其实也是较为常用的,一般在一些组件库,或者我们 去封装组件,在表单那里非常常用,其实用过antd的都能知道,antd的表单这些或者antd的protable都使用ref去控制他的一些行为。当然自己在项目中也用到过,在一个页面中将三个带有子列的可编辑表格封装成组件。
useImperativeHandle都会与fordRef去结合的使用,透传ref,非常nice,特别是在一些表单的使用场景

useImperativeHandle接受三个参数:

  • 第一个参数ref: 接受 forWardRef 传递过来的 ref
  • 第二个参数 createHandle :处理函数,返回值作为暴露给父组件的ref对象。
  • 第三个参数 deps:依赖项 deps,依赖项更改形成新的ref对象。
直接上代码,结合antdPro的可编辑表格使用
子组件部分代码
    const [editableKeys, setEditableRowKeys] = useState<React.Key[]>();
    const [strInt, setStrInt] = useState(() => new Set())//用来动态的控制码值定义按钮的显示
    const [dataSource, setDataSource] = useState<readonly DataSourceType[]>(() => data);//表格的数据源
    const actionRef = useRef()//可编辑表格的ref

    useImperativeHandle(ditRef, () => {
        ditRef.current.getData = async () => {
            await ditRef.current.validateFields()//触发表单验证
            if(editableKeys?.length>0)
            {
                message.error('还有未保存的数据')
                return  Promise.reject('还有未保存的数据')
            }//只有当没有可编辑行时才保存成功
            return dataSource
        }//给ref加一个触发子组件的表单验证 并返回dataSource
        return ditRef.current
    }, [dataSource, strInt,editableKeys])//父组件通过ref触发验证并取值

//其实这里的话是处理的可编辑表格,暴露出getData的方法给父组件,父组件直接通过按钮,提交时触发我三个proTable的表单验证,在提交。用editableKeys?.lengthl来校验可编辑表格未提交的数据

父组件

import { Button, Space } from 'antd';
import { useLocation, history } from '@umijs/max';
import BodyEditTable from './components/BodyEditable';
import EditTable from './components/editTable';
import ReturnEditTable from './components/ReturnEditTable'
import {  useRef, useEffect } from 'react';
import { updateInter } from '@/services/interManage/controller'

export default () => {
  const inputEditTableRef = useRef()// 输入参数表格
  const bodyEditTableRef = useRef()//请求body的输入表格
  const returnEditTableRef = useRef()//返回参数的可编辑表格
  const { state } = useLocation()//获得上一页面传入的数据

  useEffect(() => { }, [])//判断是否是编辑 是的话解析JSON数据 然后传入子组件
  const submit = async () => {
    const requestBody = await bodyEditTableRef.current.getData()
    const inputParameters = await inputEditTableRef.current.getData()
    const returnParameters = await returnEditTableRef.current.getData()
    const data = JSON.parse(state)
    await updateInter({ ...data, requestBodyList: requestBody, inputParametersList: inputParameters, returnParametersList: returnParameters })
    history.push('/inter')
  }//提交的操作


  return <>
    <EditTable ref={inputEditTableRef} />
    <BodyEditTable ref={bodyEditTableRef} />
    <ReturnEditTable ref={returnEditTableRef} />
    <Space>
      <Button key='test'>测试</Button>
      <Button key='next' onClick={() => history.push('/inter/basic', state)}>上一步</Button>
      <Button>取消</Button>
      <Button key="submit" onClick={() => submit()}>保存并退出</Button>
    </Space></>
}

 

直接通过ref绑定到子组件中的proTable,通过暴露出的getData触发对应的表单验证取值,后在提交。

这样其实当我有个可编辑表格变化时他也只会去重新渲染对应的proTable,不会引起其他两个可编辑表格的重新渲染。

 

 

当然react18也暴露了一些新的钩子。

- THE END -
0

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

共有 0 条评论