玩转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

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

相关推荐
东方小月5 分钟前
agent-skills:把资深工程师的‘工作流’,装进你的AI编程里
前端·架构·代码规范
Maynor9967 分钟前
AI Coding 零基础实战教程|第五部分:完整项目案例实操
java·前端·人工智能·claude code·ai coding
binbin_529 分钟前
React 井字棋教程:用一个小游戏理解组件和状态
前端·react.js·前端框架
梦曦i1 小时前
uni-router新推useUniEventChannel,彻底解决页面通信难题
前端·uni-app
柯克七七1 小时前
给老项目加了 TypeScript,本来只想自己爽,结果全公司代码审查标准被我抬高了
前端·vue.js·typescript
张元清1 小时前
React useObjectUrl Hook:预览文件与 Blob,不留内存泄漏(2026)
javascript·react.js
yuanlaile1 小时前
前端转 Flutter 自学完整规划,避开跨栈思维转换误区
前端·flutter·flutter跨平台开发
CN_HW1 小时前
Nginx 流量镜像配置文档-双轨国产化
服务器·前端·网络
姑苏倾城客1 小时前
Flutter中,html 与 dart 桥接沟通
javascript·flutter·html
JNX_SEMI2 小时前
ATR2652 GNSS双频LNA:0.75dB噪声系数与22dB高增益设计
前端·单片机·嵌入式硬件·物联网·硬件工程