作为前端摸爬滚打近六年的开发者,我至今记得第一次用useState时那种惊艳感------代码量直接砍半!但很快就被莫名其妙的闭包陷阱和无限循环教做人。今天就用真实踩坑经历,聊聊Hooks那些看似简单却暗藏玄机的细节。
🔥 闭包陷阱:Stale Closure
刚用useEffect时我写过这样的代码:
javascript
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
console.log(count); // 永远输出0!
setCount(count + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
return <div>{count}</div>;
}
发现计时器数字永远停在1后我才明白:useEffect的依赖数组为空,导致回调函数捕获的是初始count值。解决方案有两种:
方案A:用函数更新保证最新值
javascript
ini
setCount(prevCount => prevCount + 1);
方案B:正确声明依赖
javascript
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(timer);
}, [count]); // 依赖项补齐
⚡ 性能优化:避免重复计算
某次我封装了个数据筛选函数:
javascript
function ProductList({ products }) {
const [filter, setFilter] = useState('');
const filteredProducts = products.filter(product =>
product.name.includes(filter)
);
// 每次渲染都会重新计算filteredProducts!
}
当产品列表很大时,页面卡顿明显。这时就该useMemo出场了:
javascript
const filteredProducts = useMemo(() => {
return products.filter(product =>
product.name.includes(filter)
);
}, [products, filter]); // 仅依赖变化时重新计算
🎯 事件监听:及时清理资源
曾在组件里直接绑定事件:
javascript
function ResizeHandler() {
const handleResize = () => {
console.log(window.innerWidth);
};
window.addEventListener('resize', handleResize);
// 忘记移除监听!内存泄漏警告!
}
后来改成useEffect+清理机制才解决:
javascript
useEffect(() => {
const handleResize = () => {
console.log(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
💡 自定义Hook:逻辑复用的艺术
把业务逻辑抽成自定义Hook后,代码清爽多了:
javascript
function useApi(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return { data, loading };
}
// 在组件中直接使用
function UserPanel() {
const { data, loading } = useApi('/api/userinfo');
return loading ? <Spinner /> : <Profile data={data} />;
}
🚀 实战建议
- 依赖数组要诚实
不要为了消除警告随便写eslint-disable-next-line
,依赖项缺失往往是bug的根源 - 拆分复杂组件
超过200行的Hook组件建议拆分成多个自定义Hook,比如useUserData
+useUserActions
- 使用useCallback缓存函数
当函数作为props传递时,用useCallback
避免子组件不必要的重渲染
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!