react常用hook

什么是hook

  • hook都是一些以use开头的函数,表示钩子
  • React 官网提供了 些内置的 hook:ruseState、 useEffect、useMemo、useCallback…
  • 允许自定义 hook
  • hook使用规则:
    • 只能用于函数组件
      • 函数组件中使用
      • 自定义hook中使用
    • 需要在函数组件的顶层调用,不能在if、for、嵌套中使用
    • 需要以use开头

useState,用于勾出state数据

  • 在修改组件外侧的变量(非修改state、prop或强制更新)时,组件并不会更新,所以需要state数据
  • 语法:const [state,setState] = useState(initialState)
    • initialState 一个可选的初始state数据
    • initialState 可以是一个数值,字符串,数组或通过函数返回初始值
    • 返回值是一个数组的格式
      • 数组第一项 state 就是勾出的state数据
        • 组件第一次执行时,state的值为initialState的值
        • 组件后续的更新执行时,state的值为上一次setState()修改的值
      • 数组第二项setState表示修改这份state数据的一个函数
    • setState()可以是一个新state,也可以是一个函数来返回一个新state
setNum(num+1)
setNum(num+1)
setNum(num+1)
  • 在想要通过这种方式进行多次叠加时,结果并不会叠加,他们的num每次都是原num,因为只有在所有setState走完之后才会更新组件,state才会更新
  • 这需要通过函数的形式
setState((prev)=>prev+1)
setState((prev)=>prev+1)
setState((prev)=>prev+1)

这种方式,函数内的prev参数就是上一次修改后的state数据,才能实现叠加

Immer库使用,useImmer()

  • 当你的useState中的数据是一个多层嵌套的对象,想要修改更新他会变得麻烦,你要一层一层展开到你要修改的部位
  • 使用useImmer来代替useState,步骤如下:
    • 1.安装use-immer:npm install use-immer
    • 2.引入:import { useImmer } from ‘use-immer’
    • 3.用useImmer来勾出state数据:
const [state,setState] = useImmer({
	msg1:'一层',
	obj1:{
		msg2:'二层',
		obj2:{
			msg3:'三层',
			obj3:{
				msg4:'四层',
				name:'张三',
				age:18
			}
		}
	}
})
    • 4.更新数据,让react重新渲染:在事件函数中,setState(draft=>{draft.obj1.obj2.obj3.name=‘李四’;draft.obj1.obj2.obj3.age+=1})
    • 通过这样的方式,useImmer会在不影响未修改的值的情况下生成新的对象,来让react更新渲染,避免了深层嵌套时多次展开的重复代码
    • 如果useImmer中的数据是数组,在set修改时数组方法都可以用,而useState初始的数组中,不能用那些会修改原数组的方法

useEffect使用副作用

  • 语法:useEffect(effect,deps)
    • effect副作用函数,该函数在组件挂载完成时默认触发一次,如果deps声明的数据发生变化之后,该函数会再次触发
    • deps依赖的数据数组,当依赖项数据改变时effect会再次触发,可选参数,当不写时,任意数据(state,props)改变都会触发effect,给空数组时,任何数据更新都都不会再触发effect
function App(){
	const [num,setNum] = useState(1);
	useEffect(()=>{console.log('我被触发了')},[num])
	return (
		<button onClick={()=>setNum((prevNum)=>prevNum+1)}>{num}</button>
)}

在点击按钮修改num时,console.log会被再次触发

  • 清理effect
    • 在effect函数返回一个函数时,这个返回的函数在后续deps改变触发effect时,会先执行上一次effect返回的函数再执行effect
    • 使用场景:在effect中做了消息订阅,事件绑定,定时器等相关操作时,需要处理清理函数,
      简单说:当它第一次执行的时候定义了定时器,而deps改变再次触发执行的时候,上一个定时器就需要清除

useLayoutEffect,功能与useEffect类似

在DOM改变后调用,useLayoutEffect的执行时机要早于useEffect,
在18版本之前,useEffect修改DOM样式时可能存在闪烁问题,因为他在绘制完屏幕才执行,这时可以用useLayoutEffect就不存在这个问题
请添加图片描述

useContext上下文对象,勾出某个Context对象的内容

  • 语法:const value = useContext(context)
    • context就是某个React.createContext()创建出来的对象
    • value 返回值,返回该context对象提供的值
//引入
import { useContext, createContext } from 'react'
//创建context的对象
const MyContext = createContext()
function App(){
	return (
		//MyContext.Provider包裹下的后辈组件都能获取value值
		<MyContext.Provider value={"张三"}>
			<Hello></Hello>
		</MyContext.Provider>
	)
}
function Hello(){
	//儿辈组件,通过useContext(MyContext)获取长辈组件传的value值
	const value = useContext(MyContext)
	return (
		<div>
			<h1>Hello-{value}</h1>
			<Hello2 />
		</div>
)}
function Hello2(){
	//孙辈组件,通过useContext(MyContext)获取长辈组件传的value值
	const value = useContext(MyContext)
	return (
		<div>
			<h1>Hello2-{value}</h1>
		</div>
)}

从这可以看出,他就像vue的依赖注入(provide与inject),父组件注入数据,在后代组件中都能直接通过useContext获取到

useRef创建一个Ref对象

  • 语法:const myRef = useRef(initialValue)
    • initialValue初始值
    • myRef 返回值是个对象,myRef的值是initialValue初始值,如果在标签上标记了ref,则在挂载完成后,myRef是标记的元素节点
//引入
import { useRef, useEffect } from 'react'

function App(){
	<Hello></Hello>
}
function Hello(){
	const myRef = useRef('1')
	console.log(myRef);//这里打印的是1
	useEffect(()=>{console.log(myRef)})//这里打印的是h1的DOM节点,因为useEffect是在挂载完成时执行的,h1上给ref做了标记获取h1的DOM节点
	return (
		<div>
			<h1 ref={myRef}>Hello-{value}</h1>
		</div>
)}

Ref转发,forwardRef

  • 函数组件不能通过字符串方式的ref标记,因为字符串方式标记的,后续需要this.ref来获取,而函数组件没有this
  • 同时,ref通过useRef()标记一个函数组件也会报错,
  • 需要通过React.forwardRef()做转发
  • 语法:React.forwardRef()高阶组件,接收一个组件作为参数,返回一个新组件
    • const NewComponent = React.forwordRef(WrappendComponent)
//引入
import { useRef, forwardRef } from 'react'
//3.这里子组件接收的第二个参数myHRef,就是App组件中标记的这个被包装了的组件,相当于把ref传过来给你用,你再传回来我要的东西
function Hello(props,myHRef){
	return (
		<div>
			//4.将这个myHRef标记到组件中的标签,在App组件中的myRef就能获取到函数组件内的DOM
			<h1 ref={myHRef}>Hello</h1>
		</div>
)}
//1.调用forwardRef()对Hello组件做包装
const NewHello = forwardRef(Hello)
//2.App组件使用包装后的组件,标记ref
function App(){
	const myRef = useRef()
	return(
		<NewHello ref={myRef}></NewHello>
	)
}

这里顺序比较绕,按序号步骤来看

useImperativeHandle处理函数组件对外暴露的内容

  • 需要结合forwardRef()使用
  • 语法:useImperativeHandle(ref,()=>{return 要暴露的内容})
//引入
import { useRef, forwardRef, useImperativeHandle } from 'react'
//这里子组件接收的第二个参数myHRef,就是App组件中标记的这个被包装了的组件,相当于把ref传过来给你用,你再传回来我要的东西
const Hello = forwardRef(function(props,ref){
	useImperativeHandle(ref,()=>{return '123'})
	return (
		<div>
			<h1>Hello</h1>
		</div>
	)}
)

//App组件使用包装后的组件,标记ref
function App(){
	const myRef = useRef()
	return(
		<Hello ref={myRef}></Hello>
	)
}
  • 通过这个方式,myRef对象中有个current拿到的就是子组件useImperativeHandle中第二个参数暴露出来的东西
  • 可以通过这个方式,暴露一些方法让外部的组件能够操作该组件

React.memo()

  • 一个高阶组件,用于函数组件,让函数组件有缓存
  • 类似于类组件中使用PureComponent

React.useMemo(计算函数,deps)让函数组件有缓存,可以当vue的计算属性使用

  • 结合React.memo使用
  • 语法:const data = useMemo(()=>{计算函数,相当于vue计算属性中的get},[依赖数组])
    • 计算函数,必须有返回值否则useMemo得到的结果为undefined,该函数默认触发一次,后续由依赖数组变化时触发
    • deps依赖数组,计算函数中用到的数据,都需要设置为deps的项
    • data就是useMemo的返回值,有缓存

React.useCallback()缓存函数

  • 结合React.memo使用
  • 类似于useMemo,但是useCallback缓存的是函数,useMemo缓存的是值
  • 语法:const fn = useCallback(函数,deps)
    • 函数,需要被缓存的函数
    • deps,依赖数组,当依赖数组发生变化,缓存会重新执行
    • fn,缓存的函数

useReducer()

  • 语法:const [state,dispatch] = useReducer(reducer,initState)
    • initState 初始的state数据
    • reducer 一个函数,接收两个参数(state,action)
      该函数在调用dispatch时触发
      state 表示上一次的state数据
      action 表示动作,也就是dispatch调用时传递过来的action
      该函数需要返回一个全新的state数据,返回的state数据,会给到state状态数据
    • state 状态数据
      默认初始由initState决定
      后续由reducer的返回值决定
    • dispatch 派发动作的函数
      dispatch(action)
      action 是一个动作,一个包含有type属性的一个对象,type值是动作函数名

useDebugValue()

  • 语法:useDebugValue(value,format?)
  • 作用:
    • 用于自定义hook,在devtools中有个自定义的提示,这个提示值就是value值
    • format可选的格式化函数,该函数把value作为参数,它的返回值成为devtools的提示值
  • 自定义hook就是定义一个以use开头的函数,这个函数中也可以使用其他自定义hook或内置hook

useId()用于生成唯一id

useDeferredValue()获取一个延迟的值

  • 性能优化,降低某次状态更新的优先级
  • 语法:const deferredValue = useDeferredValue(value)
    • value 要基于什么数据,一般是一个state或prop数据
    • deferredValue 基于value的一个延迟值
      • 初始化阶段,deferredValue的值等于value
      • 更新阶段,如果value有更新,deferredValue会引起组件两次更新
        1.第一次的值是value上一次的值
        2.第二次的值是value最新的值

useTransition()使用过度

  • 性能优化,降低某次状态更新的优先级
  • 语法:const [isPending,startTransition] = useTransition()
    • isPending 表示当前是否有状态更新处理过度状态,一个布尔值
    • startTransition 一个函数,接收一个callback做为参数,callback内部执行的状态

useSyncExternalStore()订阅外部数据的变化

  • 语法:const state = useSyncExternalStore(subscrie,getSnapshot)
      1. subscribe - 订阅函数,内部需要返回取消订阅的函数
        初始化阶段,会触发 subscribe 函数一次,且会携带一个 listener(订阅者) 过去。
        在该函数中,需要将 listener 做保存,以供后续通知他们我的数据有变化,
      1. getSnapshot - 获取快照的一个函数,该函数内部需要返回一份数据,返回的数据就是 state
        当外部源通知订阅者有数据变化之后,订阅者会重新调用 getSnapshot 从而得到外部数据源中最新的数据
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值