玩转React Hooks

作为前端摸爬滚打近六年的开发者,我至今记得第一次用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} />;
}

🚀 实战建议

  1. 依赖数组要诚实
    不要为了消除警告随便写eslint-disable-next-line,依赖项缺失往往是bug的根源
  2. 拆分复杂组件
    超过200行的Hook组件建议拆分成多个自定义Hook,比如useUserData + useUserActions
  3. 使用useCallback缓存函数
    当函数作为props传递时,用useCallback避免子组件不必要的重渲染

⭐ 写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

✅ 一起探讨技术加qq交流群:906392632

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

相关推荐
weixin_493503674 小时前
Vue3 前端生成 PDF:会员证书与活动签到表的三种打印方案与踩坑记录
前端·pdf·状态模式
李少兄6 小时前
JavaScript 数据类型完全指南
开发语言·javascript·ecmascript
Java陈序员7 小时前
轻量运维面板!一款现代化的服务器控制面板工具!
运维·服务器·python·react.js·github
尾善爱看海7 小时前
前端算法与手写题集
前端·算法
徐小夕8 小时前
JitWord 4.0 万字分享:从协同工具到AI Word操作系统,聊聊3年产品创业史
前端·vue.js·后端
shmily麻瓜小菜鸡9 小时前
JS/TS 易踩坑知识点 — 模块化与工程化类
开发语言·javascript·ecmascript
萧鼎9 小时前
Python 高性能Web框架神器 FastAPI:自动生成API文、基于Pydant、异步请求处理全搞定
前端·python·fastapi
IT_陈寒10 小时前
Redis误用keys命令把生产环境搞崩了,血的教训
前端·人工智能·后端
计算机魔术师10 小时前
5000亿估值冲刺科创板,DeepSeek 为何急着上市?
前端
我血条子呢10 小时前
前端解析word方案
前端·word