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

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

相关推荐
haogexiaole15 分钟前
vue知识点总结
前端·javascript·vue.js
哆啦A梦15882 小时前
[前台小程序] 01 项目初始化
前端·vue.js·uni-app
智码看视界3 小时前
老梁聊全栈系列:(阶段一)架构思维与全局观
java·javascript·架构
小周同学@5 小时前
谈谈对this的理解
开发语言·前端·javascript
Wiktok5 小时前
Pyside6加载本地html文件并实现与Javascript进行通信
前端·javascript·html·pyside6
一只小风华~5 小时前
Vue:条件渲染 (Conditional Rendering)
前端·javascript·vue.js·typescript·前端框架
柯南二号5 小时前
【大前端】前端生成二维码
前端·二维码
程序员码歌5 小时前
明年35岁了,如何破局?说说心里话
android·前端·后端
博客zhu虎康6 小时前
React Hooks 报错?一招解决useState问题
前端·javascript·react.js
灰海6 小时前
vue中通过heatmap.js实现热力图(多个热力点)热区展示(带鼠标移入弹窗)
前端·javascript·vue.js·heatmap·heatmapjs