前言

自 React 16.8 引入 Hooks 以来,它们已经成为函数式组件开发的核心。然而,Hooks 的强大功能也伴随着特定的规则和陷阱。

本文旨在深入探讨 React Hooks 的使用注意事项,帮助你避免常见错误并写出更健壮的代码。

一、遵守 Hooks 的核心规则(违反这些规则,你的应用离崩溃就不远了哦)

1.只能在最顶层调用 Hooks

错误示例:​在条件语句中调用 Hooks

function BadExample({ condition }) {
  if (condition) {
    const [value, setValue] = useState(''); // 🔴 违反规则!
  }
  return <div>...</div>;
}

后果:React 内部依赖 Hook 的调用顺序来管理状态,条件渲染会彻底破坏这个机制,导致状态错乱、组件崩溃!

正确做法:​

function GoodExample({ condition }) {
  const [value, setValue] = useState(''); // ✅ 在顶层调用
  
  useEffect(() => {
    if (condition) {
      // 在 effect 内部处理条件逻辑
      setValue('some value');
    }
  }, [condition]);

  return <div>...</div>;
}

2.只在 React 函数中调用 Hooks

错误示例:

function regularJavaScriptFunction() {
  const [state, setState] = useState(''); // 🔴 违反规则!
  return state;
}

正确做法:在组件或者自定义hook中使用

function customHook() { // ✅ 自定义 Hook
  const [state, setState] = useState('');
  return state;
}

function MyComponent() {
  const value = customHook(); // ✅ 在组件中调用
  return <div>{value}</div>;
}

二、useEffect 的注意事项(useEffect 的几个致命陷阱——你中了几个呢)

陷阱1:缺失依赖导致的幽灵bug

function Component({ id }) {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    fetchData(id).then(setData);
  }, []); // 🔴 缺少 id 依赖

  return <div>{data}</div>;
}

正确做法:​

function Component({ id }) {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    fetchData(id).then(setData);
  }, [id]); // ✅ 包含所有依赖

  return <div>{data}</div>;
}

陷阱2:无限循环的死亡漩涡

function InfiniteHell() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    setCount(count + 1); // 🔁 无限地狱开启!
  }, [count]); // count 变化 → 触发 effect → 更新 count → 无限循环

  return <div>欢迎来到地狱第{count}层</div>;
}

诶嘿,虽然不想承认,但这个错误,我在学习的时候还真犯过,不只是在useEffect中,如果你在组件顶层直接调用setCount(count+1)也会导致组件无限渲染

解决方案:

function SafeComponent() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    // 使用函数式更新避免依赖 count
    setCount(prevCount => prevCount + 1);
  }, []); // ✅ 空依赖数组

  return <div>{count}</div>;
}

陷阱3:清理副作用

重要但常被忽略:

function ChatRoom({ roomId }) {
  useEffect(() => {
    const connection = createConnection(roomId);
    connection.connect();
    
    return () => {
      connection.disconnect(); // ✅ 必要的清理
    };
  }, [roomId]);

  return <div>Chat Room</div>;
}

还有定时器,不及时清理可能会导致内存泄漏,这个你们应该都知道的对吧

三、 useState 的常见陷阱

——这东西90%的人不知道(你问这90%的数据从哪来的,嗯,吹的)

1.状态更新是异步的

错误理解:

function Counter() {
  const [count, setCount] = useState(0);
  
  const handleClick = () => {
    setCount(count + 1);
    console.log(count); // 🔴 🤡这里还是旧值
  };

  return <button onClick={handleClick}>Count: {count}</button>;
}

没座,这个错误我又犯过

终极解决方案:​

function Counter() {
  const [count, setCount] = useState(0);
  
  const handleClick = () => {
    setCount(prevCount => prevCount + 1); // ✅ 函数式更新
    // 或者如果需要基于新值做操作,使用 useEffect
  };

  useEffect(() => {
    console.log('Count updated:', count); // ✅ 在这里使用新值
  }, [count]);

  return <button onClick={handleClick}>Count: {count}</button>;
}

2.对象/数组状态的正确更新

错误示例:

function UserProfile() {
  const [user, setUser] = useState({ name: '', age: 0 });
  
  const updateName = (newName) => {
    user.name = newName; // 🔴 直接修改状态!
    setUser(user); // 不会触发重新渲染
  };
}

正确做法:

function UserProfile() {
  const [user, setUser] = useState({ name: '', age: 0 });
  
  const updateName = (newName) => {
    setUser(prevUser => ({
      ...prevUser,        // ✅ 创建新对象
      name: newName      // ✅ 更新特定属性
    }));
  };
  
  const addHobby = (newHobby) => {
    setUser(prevUser => ({
      ...prevUser,
      hobbies: [...(prevUser.hobbies || []), newHobby] // ✅ 数组合并
    }));
  };
}

总结:

  1. ​✅ 所有 Hooks 都在顶层调用​

  2. ​✅ useEffect 依赖数组完整且正确​

  3. ​✅ 状态更新使用不可变模式​

  4. ​✅ 必要的清理副作用​

记住:​​强大的力量伴随着巨大的责任​​。React Hooks 给了我们前所未有的开发体验,但也要求我们更加注意代码的质量和规范。

现在,你已经掌握了避免这些常见陷阱的知识。下次当你写出 useEffect或 useState时,不妨回头看看这篇文章,确保你的代码既强大又安全。

梅座,我是断章🐕,大概还有四五个注意事项没写吧,我会放到下一篇文章

更多推荐