React Hooks 的优势和使用场景

155 阅读2分钟

React Hooks 的优势和使用场景

1. Hooks 的核心优势

1.1 逻辑复用更简单

传统高阶组件(HOC)和render props会导致组件树嵌套过深,而Hooks可以通过自定义Hook实现逻辑复用:

// 自定义Hook示例
function useWindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });

  useEffect(() => {
    const handleResize = () => setSize({
      width: window.innerWidth,
      height: window.innerHeight
    });
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return size;
}

// 在多个组件中使用
function ComponentA() {
  const size = useWindowSize();
  // ...
}

function ComponentB() {
  const size = useWindowSize();
  // ...
}

1.2 解决class组件痛点

  • 避免this绑定问题
  • 生命周期逻辑分散问题
  • 更好的TS类型推断

1.3 代码更简洁

相同功能下,Hooks版本通常比class组件少30%代码量:

// Class组件
class Counter extends React.Component {
  state = { count: 0 };
  
  componentDidMount() {
    document.title = `Count: ${this.state.count}`;
  }
  
  componentDidUpdate() {
    document.title = `Count: ${this.state.count}`;
  }

  render() {
    return (
      <button onClick={() => this.setState({ count: this.state.count + 1 })}>
        Count: {this.state.count}
      </button>
    );
  }
}

// Hooks版本
function Counter() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

2. 常用Hooks使用场景

2.1 useState - 状态管理

适用场景:

  • 组件内部状态
  • 表单控件状态
  • 简单的UI状态切换
function Form() {
  const [name, setName] = useState('');
  const [age, setAge] = useState(18);

  return (
    <form>
      <input value={name} onChange={e => setName(e.target.value)} />
      <input 
        type="number" 
        value={age}
        onChange={e => setAge(Number(e.target.value))}
      />
    </form>
  );
}

2.2 useEffect - 副作用处理

适用场景:

  • 数据获取
  • 订阅事件
  • 手动DOM操作
  • 定时器管理
function DataFetcher({ id }) {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    let isMounted = true;
    const fetchData = async () => {
      const result = await fetch(`/api/data/${id}`);
      if (isMounted) setData(await result.json());
    };
    
    fetchData();
    
    return () => {
      isMounted = false; // 清理操作
    };
  }, [id]); // 依赖数组

  return <div>{JSON.stringify(data)}</div>;
}

2.3 useContext - 跨组件共享状态

适用场景:

  • 主题切换
  • 用户认证信息
  • 多语言国际化
const ThemeContext = React.createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  const theme = useContext(ThemeContext);
  return <div style={{ background: theme === 'dark' ? '#333' : '#fff' }} />;
}

2.4 useReducer - 复杂状态逻辑

适用场景:

  • 状态逻辑复杂
  • 包含多个子值
  • 下一个状态依赖前一个状态
function todosReducer(state, action) {
  switch (action.type) {
    case 'add':
      return [...state, { text: action.text, completed: false }];
    case 'toggle':
      return state.map((todo, i) => 
        i === action.index ? {...todo, completed: !todo.completed} : todo
      );
    default:
      return state;
  }
}

function TodoList() {
  const [todos, dispatch] = useReducer(todosReducer, []);
  
  return (
    <div>
      {todos.map((todo, i) => (
        <div key={i} onClick={() => dispatch({ type: 'toggle', index: i })}>
          {todo.text}
        </div>
      ))}
      <button onClick={() => dispatch({ type: 'add', text: 'New todo' })}>
        Add Todo
      </button>
    </div>
  );
}

2.5 useMemo/useCallback - 性能优化

适用场景:

  • 计算昂贵的值
  • 避免不必要的子组件重渲染
  • 稳定的回调引用
function ExpensiveComponent({ list, filter }) {
  const filteredList = useMemo(() => {
    return list.filter(item => item.includes(filter));
  }, [list, filter]); // 只有依赖变化时重新计算

  const handleClick = useCallback(() => {
    console.log('Clicked with filter:', filter);
  }, [filter]); // 保持稳定的函数引用

  return (
    <div>
      {filteredList.map(item => (
        <div key={item} onClick={handleClick}>{item}</div>
      ))}
    </div>
  );
}

3. 最佳实践

  1. Hooks调用规则

    • 只在React函数组件或自定义Hook中调用
    • 只在最顶层调用,不能在条件、循环或嵌套函数中调用
  2. 依赖数组优化

    • useEffect/useMemo/useCallback的依赖数组要完整
    • 使用eslint-plugin-react-hooks检查依赖
  3. 自定义Hook封装

    • 以use开头的命名约定
    • 可以组合多个基础Hook
  4. 性能考量

    • 避免在渲染函数中进行昂贵计算
    • 合理使用useMemo/useCallback
    • 大型列表使用虚拟滚动
  5. 测试策略

    • 使用@testing-library/react-hooks测试自定义Hook
    • 注意异步effect的测试

4. 总结

React Hooks通过函数式的方式简化了状态管理和副作用处理,主要优势包括:

  • 更简洁的代码结构
  • 更好的逻辑复用
  • 更直观的组件编写方式
  • 更友好的TypeScript支持

典型使用场景包括:

  • 组件状态管理(useState)
  • 副作用处理(useEffect)
  • 上下文共享(useContext)
  • 复杂状态逻辑(useReducer)
  • 性能优化(useMemo/useCallback)

随着React 18的并发特性推出,Hooks将成为未来React开发的标准模式。建议新项目全面采用Hooks,老项目可以逐步迁移。