日期:2024年12月3日

关于Hook

关于React中的钩子函数,我们已经非常熟悉了。钩子函数的功能非常的强大,而它的使用又十分简单。关于钩子函数的使用,我们只需记住两点:

  1. 钩子只能在React组件和自定义钩子中使用
  2. 钩子不能在嵌套函数或其他语句(if、switch、white、for等)中使用

React中自带的钩子函数

  1. useState
  2. useEffect
  3. useContext
  4. useReducer
  5. useCallback
  6. useRef
  7. useMemo
  8. useImperativeHandle
  9. useLayoutEffect
  10. useDebugValue(18.0新增)
  11. useDeferredValue(18.0新增)
  12. useTransition(18.0新增)
  13. useId(18.0新增)
  14. useSyncExternalStore(18.0新增)
  15. useInsertionEffect(18.0新增)

useMemo

useMemo和useCallback十分相似,useCallback用来缓存函数对象,useMemo用来缓存函数的执行结果。在组件中,会有一些函数具有十分的复杂的逻辑,执行速度比较慢。闭了避免这些执行速度慢的函数返回执行,可以通过useMemo来缓存它们的执行结果,像是这样:

const result = useMemo(()=>{
    return 复杂逻辑函数();
},[依赖项])

useMemo中的函数会在依赖项发生变化时执行,注意!是执行,这点和useCallback不同,useCallback是创建。执行后返回执行结果,如果依赖项不发生变化,则一直会返回上次的结果,不会再执行函数。这样一来就避免复杂逻辑的重复执行。

useImperativeHandle

在React中可以通过forwardRef来指定要暴露给外部组件的ref:

const MyButton = forwardRef((props, ref) => {
return <button ref={ref}>自定义按钮</button>
});

上例中,MyButton组件将button的ref作为组件的ref向外部暴露,其他组件在使用MyButton时,就可以通过ref属性访问:

<MyButton ref={btnRef}/>

通过useImperativeHandle可以手动的指定ref要暴露的对象,比如可以修改MyButton组件如下:

const MyButton = forwardRef((props, ref) => {

useImperativeHandle(ref,()=> {
return {
name:'孙悟空'
};
});

return <button>自定义按钮</button>
});

useImperativeHandle的第二个参数是一个函数,函数的返回值会自动赋值给ref(current属性)。上例中,我们将返回值为{name:'孙悟空'},当然返回孙悟空没有什么意义。实际开发中,我们可以将一些操作方法定义到对象中,这样可以有效的减少组件对DOM对象的直接操作。

const MyButton = forwardRef((props, ref) => {

    const btnRef = useRef();

    useImperativeHandle(ref,()=> {
        return {
            setDisabled(){
                btnRef.current.disabled = true;
            }
        };
    });

    return <button ref={btnRef}>自定义按钮</button>
});

const App = () => {
    
    const btnRef = useRef();

    const clickHandler = () => {
        btnRef.current.setDisabled();
    };

    return <div>
        <MyButton ref={btnRef}/>
        <button onClick={clickHandler}>点击</button>
    </div>;
};

useLayoutEffect

useLayoutEffect的方法签名和useEffect一样,功能也类似。不同点在于,useLayoutEffect的执行时机要早于useEffect,它会在DOM改变后调用。在老版本的React中它和useEffect的区别比较好演示,React18中,useEffect的运行方式有所变化,所以二者区别不好演示。

useLayoutEffect使用场景不多,实际开发中,在effect中需要修改元素样式,且使用useEffect会出现闪烁现象时可以使用useLayoutEffect进行替换。

useDebugValue

用来给自定义钩子设置标签,标签会在React开发工具中显示,用来调试自定义钩子,不常用。

useDeferredValue

useDeferredValue用来设置一个延迟的state,比如我们创建一个state,并使用useDeferredValue获取延迟值:

const [queryStr, setQueryStr] = useState('');
const deferredQueryStr = useDeferredValue(queryStr);

上边的代码中queryStr就是一个常规的state,deferredQueryStr就是queryStr的延迟值。设置延迟值后每次调用setState后都会触发两次组件的重新渲染。第一次时,deferredQueryStr的值是queryStr修改前的值,第二次才是修改后的值。换句话,延迟值相较于state来说总会慢一步更新。

延迟值可以用在这样一个场景,一个state需要在多个组件中使用。一个组件的渲染比较快,而另一个组件的渲染比较慢。这样我们可以为该state创建一个延迟值,渲染快的组件使用正常的state优先显示。渲染慢的组件使用延迟值,慢一步渲染。当然必须结合React.memo或useMemo才能真正的发挥出它的作用。

useTransition

当我们在组件中修改state时,会遇到复杂一些的state,当修改这些state时,甚至会阻塞到整个应用的运行,为了降低这种state的影响,React为我们提供了useTransition,通过useTransition可以降低setState的优先级。

useTransition会返回一个数组,数组中有两个元素,第一个元素是isPending,它是一个变量用来记录transition是否在执行中。第二个元素是startTransition,它是一个函数,可以将setState在其回调函数中调用,这样setState方法会被标记为transition并不会立即执行,而是在其他优先级更高的方法执行完毕,才会执行。

除了useTransition外,React还直接为为我们提供了一个startTransition函数,在不需要使用isPending时,可以直接使用startTransition也可以达到相同的效果。

useId

生成唯一id,使用于需要唯一id的场景,但不适用于列表的key。

5 12 投票数
文章评分
订阅评论
提醒
guest

3 评论
最旧
最新 最多投票
内联反馈
查看所有评论
最爱超哥
最爱超哥
2 年 前

催更啦,react项目啥时候上线啊,超哥

超哥粉丝
1 年 前
回复给  最爱超哥

超哥迷弟
超哥迷弟
2 年 前

超哥,react视频下面链接只有前71集的代码

3
0
希望看到您的想法,请您发表评论x