React Hooks 的优势和使用场景

106 阅读3分钟

React Hooks 的优势和使用场景

1. React Hooks 的核心优势

1.1 逻辑复用更便捷

  • 传统高阶组件(HOC)和render props模式会导致组件嵌套过深
  • 自定义Hook可以轻松提取和复用状态逻辑
  • 示例:多个组件共享鉴权逻辑
// 自定义Hook示例
function useAuth() {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged(user => {
      setUser(user);
    });
    return unsubscribe;
  }, []);

  return user;
}

// 组件中使用
function Profile() {
  const user = useAuth();
  // ...
}

1.2 简化组件代码

  • 消除类组件的样板代码(constructor, this绑定等)
  • 相关逻辑可以组织在一起而非分散在不同生命周期
  • 示例:计数器组件对比
// 类组件
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.increment = this.increment.bind(this);
  }

  increment() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return <button onClick={this.increment}>{this.state.count}</button>;
  }
}

// 函数组件+Hooks
function Counter() {
  const [count, setCount] = useState(0);
  const increment = () => setCount(c => c + 1);
  return <button onClick={increment}>{count}</button>;
}

1.3 更细粒度的控制

  • useEffect可以针对不同副作用分别管理
  • 避免类组件中生命周期方法的耦合
  • 示例:数据获取与事件监听分离
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  
  // 数据获取
  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, [userId]);

  // 窗口事件监听
  useEffect(() => {
    const handleResize = () => console.log(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  // ...
}

2. 核心Hooks使用场景

2.1 useState - 状态管理

  • 适合组件内部简单状态
  • 替代this.state的解决方案
  • 示例:表单控制
function Form() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  const handleSubmit = e => {
    e.preventDefault();
    console.log({ name, email });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={name} onChange={e => setName(e.target.value)} />
      <input value={email} onChange={e => setEmail(e.target.value)} />
      <button type="submit">Submit</button>
    </form>
  );
}

2.2 useEffect - 副作用处理

  • 数据获取、订阅、手动DOM操作
  • 替代componentDidMount/Update/WillUnmount
  • 关键点:依赖数组控制执行时机
function DataFetcher({ url }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let isMounted = true;
    
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        if (isMounted && response.ok) {
          setData(await response.json());
          setLoading(false);
        }
      } catch (error) {
        if (isMounted) setLoading(false);
      }
    };

    fetchData();

    return () => {
      isMounted = false; // 清理操作
    };
  }, [url]); // url变化时重新获取

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

2.3 useContext - 跨组件状态共享

  • 替代Context.Consumer的嵌套写法
  • 适合主题、用户信息等全局状态
  • 示例:主题切换
const ThemeContext = React.createContext('light');

function App() {
  const [theme, setTheme] = useState('light');
  
  return (
    <ThemeContext.Provider value={theme}>
      <Toolbar />
      <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return <ThemedButton />;
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button style={{ 
    background: theme === 'dark' ? '#333' : '#EEE',
    color: theme === 'dark' ? '#FFF' : '#000'
  }}>Themed Button</button>;
}

2.4 useReducer - 复杂状态逻辑

  • 适合状态更新逻辑复杂的场景
  • 替代Redux的轻量级方案
  • 示例:购物车管理
function cartReducer(state, action) {
  switch (action.type) {
    case 'ADD_ITEM':
      return [...state, action.item];
    case 'REMOVE_ITEM':
      return state.filter(item => item.id !== action.id);
    case 'CLEAR_CART':
      return [];
    default:
      return state;
  }
}

function ShoppingCart() {
  const [cart, dispatch] = useReducer(cartReducer, []);
  
  return (
    <div>
      <button onClick={() => dispatch({
        type: 'ADD_ITEM',
        item: { id: Date.now(), name: 'New Item' }
      })}>
        Add Item
      </button>
      
      <ul>
        {cart.map(item => (
          <li key={item.id}>
            {item.name}
            <button onClick={() => dispatch({
              type: 'REMOVE_ITEM',
              id: item.id
            })}>
              Remove
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

2.5 useMemo/useCallback - 性能优化

  • useMemo: 缓存计算结果
  • useCallback: 缓存函数引用
  • 避免不必要的重新计算和渲染
function ExpensiveComponent({ list, filterText }) {
  const filteredList = useMemo(() => {
    console.log('Filtering...');
    return list.filter(item => 
      item.name.includes(filterText)
    );
  }, [list, filterText]); // 只有依赖变化时才重新计算

  const handleClick = useCallback(() => {
    console.log('Item clicked');
  }, []); // 空依赖表示函数不会改变

  return (
    <ul>
      {filteredList.map(item => (
        <li key={item.id} onClick={handleClick}>
          {item.name}
        </li>
      ))}
    </ul>
  );
}

3. 最佳实践建议

  1. Hook使用规则

    • 只在顶层调用Hook
    • 只在React函数中调用Hook
  2. 自定义Hook规范

    • 命名以use开头
    • 可以调用其他Hook
    • 明确输入输出
  3. 性能优化时机

    • 使用useMemo/useCallback避免不必要的计算
    • 拆分大组件为小组件利用React.memo
  4. 副作用管理

    • 每个useEffect只做一件事
    • 不要忘记清理操作
    • 正确处理依赖数组
  5. 状态设计原则

    • 相关状态尽量合并
    • 复杂状态考虑useReducer
    • 避免深层嵌套状态

React Hooks通过提供更直接的API来使用React特性,显著简化了组件开发模式。合理运用各种Hook可以构建出更清晰、更易维护的React应用。