🪝 别再重复造轮子了!教你偷懒:在 React 自定义 Hook

前言

React 组件时,你是不是总感觉有些逻辑似曾相识?

  • 比如,每次都要写一遍判断组件是否挂载的逻辑
  • 又比如,监听元素 hover 状态的代码复制了一次又一次
  • 再比如,组件挂载和卸载时的操作也总是那几行

React官方给出了许多hook供我们使用,比如我们常见的useEffectuseState等等,但光靠这些是不够的,今天分享一些自定义的hook,方便又高效!

🎯 场景一:我只是想知道组件 "活" 没活

你有没有遇到过这种情况:组件里的setTimeout还没跑完,组件就已经被卸载了,控制台立刻给你甩一个警告,仿佛在说 "你操作了一个不存在的组件"

别慌,咱们用useMountedState这个自定义 Hook 就能完美解决。

JavaScript 复制代码
// useMountedState.js
import { useRef, useEffect } from 'react'

export default function useMountedState() {
    const mounted = useRef(false);
    const get = () => mounted.current;
    useEffect(() => {
        mounted.current = true;
        return () => {
            mounted.current = false;
        }
    }, [])
    return get;
}

在组件里用起来就像给组件装了个 "生命检测仪"

JavaScript 复制代码
// App.jsx
import React, { useState, useEffect } from 'react'
import useMountedState from './hooks/useMountedState'

export default function App() {
    const isMounted = useMountedState();
    const [num, setNum] = useState(0);
    useEffect(() => {
        setTimeout(() => {
            // 先检查组件是否还活着,再更新状态
            if (isMounted()) {
                setNum(1);
            }
        }, 1000);
    }, []);

    return (
        <div>
            {isMounted() ? '组件挂载完成 🎉' : '组件还在编译 🛠️'}
        </div>
    )
}

刚打开浏览器(显示还在编译):

过几秒(挂载完成):

有了它,你再也不用担心在异步操作 里更新一个已经 "去世" 的组件了。

🎬 场景二:组件的 "登场" 与 "谢幕" 要仪式感

组件挂载卸载 时,我们经常需要做一些初始化和清理工作。

  • 比如页面埋点、订阅事件、定时器清理等
  • 直接用useEffect写虽然也行,但每次都要写return总觉得有点麻烦

这时候useLifecycles就派上用场了,它把组件的 "生命周期" 打包成了一个简单的接口

JavaScript 复制代码
// useLifecycles.js
import { useEffect } from 'react'

export default function useLifecycles(onMount, onUnmount) {
    useEffect(() => {
        if (onMount) {
            onMount();
        }
       return () => {
            if (onUnmount) {
                onUnmount();
            }
        }
    }, []);
}

用起来就像给组件安排了 "入场" 和 "退场" 的节目单

JavaScript 复制代码
// App2.jsx
import React, { useState } from 'react'
import useLifecycles from './hooks/useLifecycles';

const Child = () => {
    useLifecycles(
        () => {
            console.log('child组件挂载🎬');
        },
        () => {
            console.log('child组件卸载👋');   
        }
    )
    return <h1>child组件</h1>
}

export default function App2() {
    const [show, setShow] = useState(true);
    return (
        <div>
            <h1 onClick={() => setShow(!show)}>App2</h1>
            {
                show && <Child></Child>
            }
        </div>
    )
}

刚打开浏览器一定会打印child组件挂载🎬

当点击App2时,child组件消失 (卸载) ,打印child组件卸载 👋

✋ 场景三:元素 hover 状态的 "小雷达"

实现元素hover效果是前端的家常便饭,传统写法需要给元素绑定onMouseEnteronMouseLeave事件。

  • 逻辑不复杂,但写多了也烦
  • 咱们可以用useHover把这个逻辑封装成一个 Hook
JavaScript 复制代码
// useHover.jsx
import { useState, cloneElement } from 'react'

export default function useHover(element) {
    const [state, setState] = useState(false);
    const onMouseEnter = (originalOnMouseEnter) => {
        return (event) => {
            originalOnMouseEnter?.(event);
            setState(true);
        }
    };
    const onMouseLeave = (originalOnMouseLeave) => {
        return (event) => {
            originalOnMouseLeave?.(event);
            setState(false);
        }
    };
    if (typeof element === 'function') {
        element = element(state);
    }
    const el = cloneElement(element, {
        onMouseEnter: onMouseEnter(element.props.onMouseEnter),
        onMouseLeave: onMouseLeave(element.props.onMouseLeave),
    })
    return [el, state];
}

在组件里使用时,就像给元素装了个 "小雷达"

JavaScript 复制代码
// App3.jsx
import useHover from './hooks/useHover.jsx';

export default function App3() {
    const element = (hovered) => {
        return <div>
            Hover me! {hovered && 'Thanks!'}
        </div>
    }
    const [hoverable, hovered] = useHover(element);
    return (
        <div>
            {hoverable}
            {hovered ? 'yes ✅' : 'no ❌'}
        </div>
    )
}

鼠标不在Hover me!上面的时候(显示no ❌):

当鼠标🖱️移动到Hover me!上面的时候(显示yes ✅):

鼠标悬停时,元素会显示 "Thanks!",下方也会同步显示状态,交互体验直接拉满!

🚀 用别人写好的库

大家应该发现了,上面的组件都是我自己手搓的,其实已经有很多人写好了,我们只需下载然后就可以使用了。我给大家推荐一个:

地址: www.npmjs.com/package/rea...

下载:npm i react-use

里面有许多已经封装好了的hook组件,包括上面介绍的,只需引入即可:

JavaScript 复制代码
import { useMountedState } from 'react-use';
import { useHover } from 'react-use';
import { useLifecycles } from 'react-use';

结语

自定义 Hook 就像 React 世界里的 "乐高积木",把零散的逻辑拼成一个个可复用的模块。

  • 它不是什么高大上的魔法,就是把你本来要重复写的代码打包了一下
  • 不仅能让你的代码更干净,还能让你开发时少掉几根头发

下次再遇到重复逻辑时,别再 cv 了,动手写个自定义 Hook 吧!毕竟,优秀的程序员都是会 "偷懒" 的艺术家

相关推荐
踩着两条虫3 小时前
AI 驱动的 Vue3 应用开发平台 深入探究(二十三):API与参考之Engine API 参考
前端·vue.js·ai编程
程序员清风3 小时前
看完Anthropic研究才懂:你有多会问,AI就有多强!
java·后端·面试
Moment3 小时前
开源一年,我的 AI 全栈项目 AI 协同编辑器终于有 1.1 k star了 😍😍😍
前端·后端·面试
爱学习的小囧3 小时前
VCF 集群部署灵活组合:单节点与高可用配置完全指南
java·服务器·前端
一招定胜负3 小时前
机器学习 + 深度学习经典面试真题
深度学习·机器学习·面试
96773 小时前
AJAX和Axios理解和关系
前端·ajax·okhttp
sg_knight3 小时前
Claude Code 如何辅助定位 Bug 和问题代码
java·前端·bug·ai编程·claude·code·claude-code
行思理3 小时前
Linux查看网站访问IP的命令大全
linux·服务器·前端
晓13133 小时前
第四章 TypeScript 类型声明文件与 React 运用
前端·react.js·typescript