React Hooks 的优势和使用场景

83 阅读2分钟

React Hooks 的优势和使用场景

1. Hooks 的核心优势

1.1 逻辑复用更简单

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

// 自定义计数器Hook
function useCounter(initialValue) {
  const [count, setCount] = useState(initialValue);
  const increment = () => setCount(c => c + 1);
  return { count, increment };
}

// 组件A使用
function ComponentA() {
  const counter = useCounter(0);
  return <button onClick={counter.increment}>{counter.count}</button>;
}

// 组件B复用相同逻辑
function ComponentB() {
  const counter = useCounter(10);
  return <div onClick={counter.increment}>Count: {counter.count}</div>;
}

1.2 解决class组件痛点

  • 告别this绑定问题
  • 生命周期逻辑更集中
  • 避免不必要的组件拆分

1.3 性能优化更精细

function ExpensiveComponent() {
  const [value, setValue] = useState(0);
  
  // 只有value变化时才重新计算
  const memoizedValue = useMemo(() => {
    return heavyComputation(value);
  }, [value]);

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

2. 常用Hooks使用场景

2.1 useState - 状态管理

function Form() {
  const [name, setName] = useState('');
  const [age, setAge] = useState(18);

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

2.2 useEffect - 副作用处理

function DataFetcher({ id }) {
  const [data, setData] = useState(null);

  useEffect(() => {
    let isMounted = true;
    fetchData(id).then(res => {
      if(isMounted) setData(res);
    });

    return () => {
      isMounted = false; // 清除副作用
    };
  }, [id]); // id变化时重新获取

  return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
}

2.3 useContext - 跨组件通信

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' }}>
    Current theme: {theme}
  </div>;
}

2.4 useReducer - 复杂状态逻辑

function todosReducer(state, action) {
  switch (action.type) {
    case 'add':
      return [...state, action.payload];
    case 'toggle':
      return state.map(todo =>
        todo.id === action.id ? {...todo, done: !todo.done} : todo
      );
    default:
      return state;
  }
}

function TodoList() {
  const [todos, dispatch] = useReducer(todosReducer, []);
  
  return (
    <>
      <button onClick={() => dispatch({
        type: 'add',
        payload: {id: Date.now(), text: 'New todo', done: false}
      })}>
        Add Todo
      </button>
      
      {todos.map(todo => (
        <div key={todo.id}>
          {todo.text}
          <input 
            type="checkbox" 
            checked={todo.done}
            onChange={() => dispatch({type: 'toggle', id: todo.id})}
          />
        </div>
      ))}
    </>
  );
}

3. 高级应用场景

3.1 自定义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 ResponsiveComponent() {
  const { width } = useWindowSize();
  return <div>Window width: {width}px</div>;
}

3.2 性能优化组合

function ProductList({ products }) {
  const [filter, setFilter] = useState('');
  
  // 缓存过滤结果
  const filteredProducts = useMemo(() => {
    return products.filter(p => 
      p.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [products, filter]);

  // 缓存回调函数
  const handleSelect = useCallback((productId) => {
    console.log('Selected:', productId);
  }, []);

  return (
    <>
      <input 
        value={filter}
        onChange={e => setFilter(e.target.value)}
        placeholder="Filter products..."
      />
      
      <ul>
        {filteredProducts.map(product => (
          <ProductItem 
            key={product.id} 
            product={product}
            onSelect={handleSelect}
          />
        ))}
      </ul>
    </>
  );
}

4. 最佳实践

  1. 遵循Hooks调用规则

    • 只在React函数组件或自定义Hook中调用
    • 不要在循环、条件或嵌套函数中调用
  2. 合理拆分副作用

    // 不好的做法 - 混合不相关的逻辑
    useEffect(() => {
      document.title = `You clicked ${count} times`;
      fetchData(userId); // 不相关的逻辑
    }, [count, userId]);
    
    // 好的做法 - 分离副作用
    useEffect(() => {
      document.title = `You clicked ${count} times`;
    }, [count]);
    
    useEffect(() => {
      fetchData(userId);
    }, [userId]);
    
  3. 自定义Hook命名规范

    • 始终以use开头
    • 明确表达Hook的功能
  4. 性能优化时机

    • 使用useMemo处理昂贵计算
    • 使用useCallback避免不必要的子组件重渲染
  5. 渐进式采用策略

    • 新组件直接使用Hooks开发
    • 旧组件逐步重构

Hooks为React开发带来了革命性的改变,使函数组件能够拥有类组件的所有能力,同时提供了更简洁的代码组织和更灵活的逻辑复用方式。正确使用Hooks可以显著提高代码的可维护性和可测试性。