React钩子HOOK
通了一遍react之后,就剩下写项目去积累经验了。这篇博客来复盘一下常用的react函数组件的钩子以及不常用的钩子。钩子只能在react组件和自定义钩子的时候使用。
1.useState
在react中当我们需要一个state变量去影响组件的渲染结果或者希望用户操作或者副作用去改变变量刷新ui的时候用这个钩子定义state变量。
2.useEffect
用法大概就是图中的用法,在这个useEffect钩子里面我们可以去定义组件挂载完之后执行或者根据依赖项[]改变而调用里面的逻辑。里面第一个参数是回调函数,return里面是清理函数,组件卸载或者useEffect下次调用前执行。
3.useRef
容器钩子,用来在组件中保存可变数据的引用不会触发渲染,可以获取DOM元素,也可以保存定时器id这种可以在其他函数执行体内用到的全局变量。
4.useContext
用这个钩子createContext可以去创建一个context盒子保存一些数据以及操作数据的方法,这样我们可以在根组件里面把方法和方法操作的数据通过Provider去通过value传递到context里面,然后子孙组件里面用useContext把我们创建的context中的数据和方法勾出来。这里ctx这个对象就是我们根组件提供的数据和方法。
5.useReducer
我们在创建一个state数据的时候,往往会有很多操作数据的方法,比如列表的增删改查,那么我们可以用这个钩子去把数据和操作方法写在一起。代码可维护性高而且很直观。使用方法如图,创建reducer。参数是state,action通过action去判断加工的方法然后去加工state,然后用useReducer这个钩子去reducer作为参数,以及初始值作为参数,然后数组结构state获取参数中的初始值,dispatch就是派发器,我们使用派发器去触发reducer函数更新状态,参数是action。
6. useCallback
没什么好说的。当我们希望函数不会因为组件重新渲染的时候重新被定义执行,就可以用这个钩子去包裹函数,然后只有依赖改变才会重新定义执行。搭配React.memo如果父组件传递函数作为props给子组件,防止子组件没变化还渲染。优化性能
7.useMemo
当我们不希望一个函数或者一个组件因为父组件渲染而渲染的时候,我们就可以去用useMemo包裹。除非依赖项变化,否则缓存之后不会再次定义重新渲染。组件在用钩子包裹赋值给一个变量记得在父组件引用这个变量{some}。
8.useImperativeHandle
当我们希望一个场景,父组件想改变子组件的DOM元素,我们就可以用React.forwardRef去包裹子组件,然后就可以除了props接收一个ref作为参数,然后可以直接给子组件DOM元素绑定ref,但是这样会导致失控,也就是父组件可以随意修改这个DOM元素。那么我们就需要用一个useImperativeHandle这个钩子去执行回调函数,参数是ref,回调函数。然后回调函数的返回值就会成为ref。这样我们只返回改变value的方法父组件就只能修改input的value值
import React, { useImperativeHandle } from 'react'
const Some = React.forwardRef((props, ref) => {const ref2 = React.useRef()const clickHandle = () => {console.log(ref2.current.value);ref2.current.value = ''}//useImperativeHandle可以用来指定ref返回的值useImperativeHandle(ref, () => {//回调函数的返回值会成为ref// return { name: 'sun' }return {changeInputValue(value) {ref2.current.value = value}}})return (<div><h2 >Some</h2><input ref={ref2} type="text" name="" id="" /><button onClick={clickHandle}>some BTN</button></div>)
})
export default Some
9.useEffect,useInsertionEffect,useLayoutEffect。
他们几乎是相同的只不过执行的顺序有所不同。
按照这张图来使用就可以了。
10.useDeferredValue和useTransition
这两个组件都是用于处理当我们两个组件比如筛选功能。表单和列表两个组件用到同一个state,那么如果列表查找很慢的话就会影响表单无法正常输入。那么我们就可以用useDeferred Value这个钩子去以state为参数定义一个新的变量,这个变量会让页面渲染两次。因为会执行两次第一次是改变前的值,第二次是改变后的值。所以列表组件要套用一个ReactMemo防止第一次没改变的时候就重新渲染列表组件了。这样我们就可以正常的输入表单了,不会被列表组件影响,useTransition一样的道理,都是去设置一个变量只不过这个不是根据state去设置而且需要我们去设置一个state,然后startTransition这个方法去异步执行更新这个变量。也需要用memo控制因为也会渲染两次。
import React, { useEffect, useImperativeHandle, useLayoutEffect, useInsertionEffect, useDeferredValue, startTransition } from 'react'
import Some from './components/Some'
import StudentList from './components/StudentList';
import useMyHook from './Hook/useMyHook'
export default function App() {// const someRef = React.useRef()// useEffect(() => {// console.log(someRef);// // someRef.current.innerText = 'Some' + 2// someRef.current.changeInputValue('www')// })//无法直接获取react组件的dom对象//因为一个react组件含有多个dom对象//react不知道给你谁console.log('重新渲染');//useDeferredValue()需要一个state作为参数 会为该state创建一个延迟值//当设置了延迟值每次state修改都会触发两次渲染//这两次执行对于其他部分没有区别但是延迟值两次执行的值是不同的//第一次执行延迟值是state的旧值第二次是新值//延迟值会比state慢一步const [count, setCount] = React.useState(0)const deferrefValue = React.useDeferredValue(count)console.log(count, deferrefValue)// useMyHook()const ref3 = React.useRef()// useEffect(() => {// console.log('useEffect', ref3);// })// useLayoutEffect(() => {// console.log('useLayoutEffect', ref3);// })// useInsertionEffect(() => {// console.log('useInsertionEffect', ref3);// })const handle = () => {setCount(preState => preState += 1)}const [filter, setFilter] = React.useState('')const [filter2, setFilter2] = React.useState('')const [ispending, startTransition] = React.useTransition()//ispending是状态---const change = (e) => {setFilter(e.target.value)startTransition(() => {setFilter2(e.target.value)})// setFilter2(e.target.value)}// const deferredFilter = useDeferredValue(filter)return (<div><h1>App</h1><h2>result---{count}</h2><h3 ref={ref3}>我我</h3><button onClick={handle}>点我</button><Some />{/* 多个组件用一个state组件之间会互相影响一个组件卡顿导致所以组件都卡此时可以使用延迟*/}<input src="" alt="" onChange={change} value={filter} /><StudentList filter={filter2} /></div>)
}
这些就是我接触到的一些组件,写完这么多钩子函数,其实你会发现:React 真的变“灵活”了不少。
以前在 class 组件里东一块西一块的状态逻辑,现在 useState 和 useEffect 一下子就搞定;想优化性能?useMemo、useCallback 来帮你;需要访问 DOM 或跨渲染保存的值?useRef 直接搞定;甚至你还可以写自己的 Hook,把重复逻辑抽出来,真的太香了。