深入理解 React Hooks:为何出现、解决了什么问题,以及如何高质量自定义 Hook
一、React为什么要引入Hooks?
React 在 16.8 版本引入 Hooks,是为了解决函数组件和类组件之间的能力不对等问题,以及长期困扰开发者的逻辑复用和代码维护问题。
1. 类组件的复杂性
- 状态逻辑分散在多个生命周期方法中,导致代码难以维护:
 
            
            
              jsx
              
              
            
          
          class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  componentDidMount() {
    console.log('mounted');
  }
  componentDidUpdate() {
    console.log('updated');
  }
  componentWillUnmount() {
    console.log('unmounted');
  }
  render() {
    return 
    <button onClick={() => this.setState({ count: this.state.count + 1 })}>
    {this.state.count}
    </button>;
  }
}
        - 必须理解 
this,逻辑分散、结构冗长、复用困难,提升了使用门槛。 
2. 逻辑复用困难
- 高阶组件(HOC)和 render props 虽然解决了复用问题,但导致组件嵌套层级增加:
 
            
            
              jsx
              
              
            
          
          <ThemeContext.Consumer>
  {theme => (
    <UserContext.Consumer>
      {user => <Profile user={user} theme={theme} />}
    </UserContext.Consumer>
  )}
</ThemeContext.Consumer>
        - 组件嵌套地狱造成调试困难
 
3.Hooks 提供的能力
- 在函数组件中使用状态、生命周期、副作用等能力;
 - 以函数方式复用状态逻辑,无需引入额外组件结构;
 - 更好地组织代码和关注点,实现逻辑模块化和组合式开发。
 
二、React 内置 Hook 作用和使用场景
| Hook | 作用 | 典型使用场景 | 
|---|---|---|
useState | 
本地状态管理 | 计数器、表单输入 | 
useEffect | 
副作用管理 | 数据请求、订阅、DOM 操作 | 
useRef | 
持久化变量、访问 DOM | 防抖节流、缓存请求、焦点控制 | 
useMemo | 
计算缓存 | 依赖变化时才重新计算,如表格排序、过滤数据 | 
useCallback | 
缓存函数 | 组件 props 稳定性、避免重复渲染 | 
useContext | 
上下文共享 | 跨层级传递全局状态,如主题、用户信息 | 
这些 Hook 是开发中使用频率最高的基础能力,掌握它们可以覆盖 90% 的状态逻辑需求。此处简单列举了一下,想要了解hook的具体实现和使用方法,请查阅官网文档或其他文章。
三、自定义hook
1.为什么需要自定义 Hook?
"你是否曾想过:我也可以写一个组件或者工具函数来处理这些逻辑啊,为什么非要用 Hook?答案在于 ------ Hook 能更自然地融入函数组件生命周期,拥有响应式、状态管理和副作用能力,远比组件封装或工具函数更贴合 React 的思想。"
- 提取重复逻辑,避免复制粘贴(例如:分页、搜索、数据加载)
 - 分离UI和逻辑,组件更简洁
 - 增强组合性和可测试性,便于单元测试和逻辑追踪
 - 封装异步数据流、状态流转等复杂流程,更贴近实际业务
 
示例1:useDebounce防抖hook
            
            
              ts
              
              
            
          
          function useDebounce<T>(value: T, delay = 300): T {
  const [debouncedValue, setDebouncedValue] = useState(value);
  useEffect(() => {
    const timer = setTimeout(() => setDebouncedValue(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);
  return debouncedValue;
}
        
            
            
              jsx
              
              
            
          
          //应用:搜索输入节流
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 500);
useEffect(() => {
 fetch(`/api/search?q=${debouncedQuery}`);
}, [debouncedQuery]);
        示例2:usePrevious获取前一个状态
            
            
              ts
              
              
            
          
          function usePrevious<T>(value: T): T | undefined {
 const ref = useRef<T>();
 useEffect(() => {
   ref.current = value;
 }, [value]);
 return ref.current;
}
//可以用来对比状态变化、实现动画触发逻辑
        四、什么时候自定义Hook呢?
判断标准:
- 是否复用:多个组件共享逻辑
 - 是否复杂:状态多,副作用频繁
 - 是否关注点分离:UI和逻辑混杂
 - 是否组合多个Hook:多个Hook之间存在依赖关系
 
常见的封装场景:
- 数据请求(useRequest, useAxios)
 - 弹窗控制(useModal)
 - 表单逻辑(useForm)
 - 状态缓存(usePersistedState)
 - 响应式媒体查询(useMediaQuery)
 
推荐资源
- React 官方文档:react.dev/learn
 - useHooks 中文站:usehooks.com/
 - 阿里团队实践文档:developer.aliyun.com/article/125...
 - 《自定义 Hooks 使用场景分析》:www.cnblogs.com/qiaozhiming...