React Hooks 的优势和使用场景

50 阅读2分钟
# React Hooks 的优势和使用场景

## 1. 代码复用性提升
通过自定义Hook可以轻松提取组件逻辑,实现跨组件复用。传统高阶组件(HOC)和render props模式会导致组件树嵌套,而Hook直接在组件内部调用,保持组件扁平结构。

```jsx
// 自定义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 { width } = useWindowSize();
  // ...
}

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

2. 逻辑关注点分离

类组件中相关逻辑分散在不同生命周期,Hook允许按功能组织代码,将相关逻辑集中处理。

// 传统类组件方式
class Example extends React.Component {
  componentDidMount() {
    // 逻辑A初始化
    // 逻辑B初始化
  }
  
  componentDidUpdate() {
    // 逻辑A更新
    // 逻辑B更新
  }
  
  componentWillUnmount() {
    // 逻辑A清理
    // 逻辑B清理
  }
}

// Hook方式
function Example() {
  // 逻辑A相关代码
  useEffect(() => {
    // 初始化
    return () => {
      // 清理
    };
  }, [/* 依赖 */]);

  // 逻辑B相关代码
  useEffect(() => {
    // 初始化
    return () => {
      // 清理
    };
  }, [/* 依赖 */]);
}

3. 简化复杂组件

使用useReducer可以更好地管理复杂状态逻辑,替代Redux的部分场景。

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:
      throw new Error();
  }
}

function Todos() {
  const [todos, dispatch] = useReducer(todosReducer, []);
  
  const handleAdd = text => {
    dispatch({ type: 'add', payload: { id: Date.now(), text } });
  };
  
  // ...
}

4. 性能优化

useMemo和useCallback可以避免不必要的计算和子组件重渲染。

function ExpensiveComponent({ list }) {
  const sortedList = useMemo(() => {
    return list.sort((a, b) => a.value - b.value);
  }, [list]);

  const handleClick = useCallback(() => {
    // 处理点击
  }, [/* 依赖 */]);

  return <Child onClick={handleClick} list={sortedList} />;
}

5. 副作用管理

useEffect统一处理副作用,替代componentDidMount、componentDidUpdate和componentWillUnmount。

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变化时重新获取
}

6. 使用场景建议

  • useState: 管理简单组件状态
  • useEffect: 处理副作用、订阅、定时器等
  • useContext: 跨组件共享状态
  • useReducer: 管理复杂状态逻辑
  • useMemo/useCallback: 性能优化
  • 自定义Hook: 封装可复用逻辑

7. 注意事项

  1. 只在顶层调用Hook,不要在循环、条件或嵌套函数中调用
  2. 仅在React函数组件或自定义Hook中调用Hook
  3. 使用eslint-plugin-react-hooks确保规则正确使用
  4. 复杂状态逻辑考虑使用useReducer替代多个useState
  5. 避免在useEffect中执行阻塞渲染的操作