React Hooks 避坑指南

4 阅读2分钟

Hooks 避坑指南:5 个常见错误及解决方案

错误 1:在条件语句中使用 Hooks

❌ 错误写法:

function UserProfile({ showEmail }) {
  const [name, setName] = useState('');
  
  if (showEmail) {
    const [email, setEmail] = useState(''); // 错误!
  }
  
  return <div>{name}</div>;
}

✅ 正确写法:

function UserProfile({ showEmail }) {
  const [name, setName] = useState('');
  const [email, setEmail] = useState(''); // 始终定义
  
  return <div>{name}{showEmail && email}</div>;
}

原因: Hooks 必须在顶层调用,条件执行会破坏调用顺序。


错误 2:useEffect 依赖项缺失

❌ 错误写法:

function Counter() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    const timer = setInterval(() => {
      setCount(count + 1); // 始终是旧值!
    }, 1000);
    return () => clearInterval(timer);
  }, []); // 缺少 count 依赖
  
  return <div>{count}</div>;
}

✅ 正确写法:

function Counter() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    const timer = setInterval(() => {
      setCount(prev => prev + 1); // 使用函数式更新
    }, 1000);
    return () => clearInterval(timer);
  }, []);
  
  return <div>{count}</div>;
}

技巧: 使用函数式更新 setState(prev => ...) 可避免依赖问题。


错误 3:useEffect 中直接修改状态

❌ 错误写法:

function TodoList() {
  const [todos, setTodos] = useState([]);
  
  useEffect(() => {
    const newTodo = { id: 1, text: 'Learn Hooks' };
    todos.push(newTodo); // 直接修改!
    setTodos(todos);
  }, []);
  
  return <div>{todos.length}</div>;
}

✅ 正确写法:

function TodoList() {
  const [todos, setTodos] = useState([]);
  
  useEffect(() => {
    setTodos(prev => [...prev, { id: 1, text: 'Learn Hooks' }]);
  }, []);
  
  return <div>{todos.length}</div>;
}

原因: React 依赖不可变性检测变化,直接修改不会触发重渲染。


错误 4:useCallback/useMemo 滥用

❌ 错误写法:

function Form({ onSubmit }) {
  const [value, setValue] = useState('');
  
  // 不必要的记忆化
  const handleChange = useCallback((e) => {
    setValue(e.target.value);
  }, []);
  
  const doubled = useMemo(() => value * 2, [value]);
  
  return <input onChange={handleChange} />;
}

✅ 正确写法:

function Form({ onSubmit }) {
  const [value, setValue] = useState('');
  
  // 简单场景直接定义
  const handleChange = (e) => setValue(e.target.value);
  
  return <input onChange={handleChange} />;
}

技巧: 仅在传递回调给子组件或计算开销大时使用记忆化。


错误 5:自定义 Hooks 命名不规范

❌ 错误写法:

function useUserData() { // 不是真正的 Hook
  const userData = fetchUser();
  return userData;
}

function fetchData() { // 应该是 Hook
  const [data, setData] = useState(null);
  return data;
}

✅ 正确写法:

function fetchUserData() { // 普通函数
  return fetchUser();
}

function useUserData() { // 自定义 Hook
  const [data, setData] = useState(null);
  return data;
}

规则: 使用 Hooks 的函数必须以 use 开头。


📌 核心要点总结

  1. 顶层调用 — 永远不要在条件、循环或嵌套函数中调用 Hooks
  2. 完整依赖 — useEffect/useMemo/useCallback 的依赖数组要完整
  3. 不可变更新 — 始终创建新对象/数组,不要直接修改
  4. 适度优化 — 不要过早使用 useCallback/useMemo
  5. 命名规范 — 自定义 Hooks 必须以 use 开头