前言
写 React 组件时,你是不是总感觉有些逻辑似曾相识?
- 比如,每次都要写一遍判断组件是否挂载的逻辑
- 又比如,监听元素
hover状态的代码复制了一次又一次 - 再比如,组件挂载和卸载时的操作也总是那几行
React官方给出了许多hook供我们使用,比如我们常见的useEffect,useState等等,但光靠这些是不够的,今天分享一些自定义的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效果是前端的家常便饭,传统写法需要给元素绑定onMouseEnter和onMouseLeave事件。
- 逻辑不复杂,但写多了也烦
- 咱们可以用
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吧!毕竟,优秀的程序员都是会 "偷懒" 的艺术家。