React Hooks 的优势和使用场景

127 阅读3分钟

React Hooks 的优势和使用场景

1. Hooks 的核心优势

  1. 简化组件逻辑

    • 将相关逻辑集中处理,告别生命周期方法的分散
    • 示例:将数据获取和事件监听放在同一个 useEffect
  2. 代码复用性提升

    • 自定义 Hook 可以轻松复用状态逻辑
    • 示例:创建 useFetch 处理所有数据请求
  3. 函数式组件全面能力

    • 无需类组件即可使用 state 和其他 React 特性
    • 示例:useState 让函数组件拥有状态能力
  4. 更清晰的代码结构

    • 按功能而非生命周期组织代码
    • 示例:相关逻辑集中在一个 useEffect 而非分散在多个生命周期方法
// 传统类组件 vs Hooks 函数组件对比
class Example extends React.Component {
  state = { count: 0 };
  
  componentDidMount() {
    document.title = `点击了 ${this.state.count} 次`;
  }
  
  componentDidUpdate() {
    document.title = `点击了 ${this.state.count} 次`;
  }
  
  render() {
    return (
      <button onClick={() => this.setState({ count: this.state.count + 1 })}>
        点击 {this.state.count} 次
      </button>
    );
  }
}

// Hooks 版本
function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `点击了 ${count} 次`;
  }, [count]);

  return (
    <button onClick={() => setCount(count + 1)}>
      点击 {count} 次
    </button>
  );
}

2. 核心 Hooks 使用场景

useState

  • 场景:组件内部状态管理
  • 最佳实践
    • 复杂状态建议拆分为多个 useState
    • 相关状态可考虑使用 useReducer
function Form() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  
  return (
    <form>
      <input value={name} onChange={e => setName(e.target.value)} />
      <input value={email} onChange={e => setEmail(e.target.value)} />
    </form>
  );
}

useEffect

  • 场景:副作用处理
    • 数据获取
    • 事件订阅
    • 手动 DOM 操作
  • 最佳实践
    • 明确依赖数组
    • 清理副作用
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    let isMounted = true;
    
    fetchUser(userId).then(data => {
      if (isMounted) setUser(data);
    });
    
    return () => {
      isMounted = false;
    };
  }, [userId]);
  
  return <div>{user?.name}</div>;
}

useContext

  • 场景:跨组件共享状态
  • 最佳实践
    • 搭配 useReducer 实现小型状态管理
    • 避免过度使用导致组件难以维护
const ThemeContext = 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' }} />;
}

useReducer

  • 场景:复杂状态逻辑
    • 多个子值相互依赖
    • 下一个状态依赖前一个状态
  • 最佳实践
    • 适合表单状态管理
    • 替代多个 useState
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 (
    <>
      {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>
    </>
  );
}

3. 自定义 Hook 实践

  1. 提取通用逻辑

    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 MyComponent() {
      const { width } = useWindowSize();
      return <div>窗口宽度: {width}px</div>;
    }
    
  2. 组合多个 Hook

    function useUserData(userId) {
      const [user, setUser] = useState(null);
      const [loading, setLoading] = useState(false);
      const [error, setError] = useState(null);
      
      useEffect(() => {
        setLoading(true);
        fetchUser(userId)
          .then(data => {
            setUser(data);
            setError(null);
          })
          .catch(err => setError(err))
          .finally(() => setLoading(false));
      }, [userId]);
      
      return { user, loading, error };
    }
    

4. 性能优化技巧

  1. useMemo

    • 场景:计算昂贵的值
    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
    
  2. useCallback

    • 场景:避免不必要的子组件重渲染
    const handleClick = useCallback(() => {
      doSomething(a, b);
    }, [a, b]);
    
  3. React.memo

    • 场景:组件浅比较
    const MyComponent = React.memo(function MyComponent(props) {
      /* 渲染使用 props */
    });
    

5. 注意事项

  1. Hook 调用规则

    • 只在最顶层调用 Hook
    • 只在 React 函数中调用 Hook
  2. 依赖数组

    • 确保包含所有依赖项
    • 使用 eslint-plugin-react-hooks 检查
  3. 性能陷阱

    • 避免在渲染函数中执行高开销操作
    • 合理拆分组件避免过度渲染
  4. 渐进采用策略

    • 新组件优先使用 Hooks
    • 旧组件逐步重构

6. 总结

React Hooks 通过以下方式改变了 React 开发范式:

  • 逻辑复用更简单
  • 代码组织更直观
  • 组件测试更容易
  • 性能优化更精准

对于新项目,建议全面采用 Hooks 开发;对于现有项目,可以逐步迁移。掌握 Hooks 是现代化 React 开发的必备技能。