让我们聊一聊 React Hooks

145 阅读2分钟

一、Hooks的诞生背景(Why Hooks?)

  1. 类组件的痛点

    • 状态逻辑难以复用(render props/HOC导致嵌套地狱)
    • 生命周期割裂逻辑(如componentDidMountcomponentDidUpdate中重复代码)
    • this指向问题和类对编译优化的阻碍
  2. 函数组件的局限

    • 无状态、无生命周期能力(Hooks出现前的函数组件仅适合展示型组件)
// 类组件状态管理 VS 函数组件+Hooks
class Counter extends React.Component {
  state = { count: 0 };
  handleClick = () => this.setState({ count: this.state.count + 1 });
  render() { return <button onClick={this.handleClick}>{this.state.count}</button> }
}

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

二、核心Hooks原理解析

  1. useState - 状态管理

    • 闭包存储原理:通过闭包保存状态,依赖调用顺序保证状态对应
    • 异步批量更新:多状态变更合并渲染,可用函数式更新保证准确性
    const [state, setState] = useState(initialState);
    setState(prev => prev + 1); // 函数式更新解决闭包陷阱
    
  2. useEffect - 副作用管理

    • 执行时机:DOM更新后异步执行(useLayoutEffect同步执行)
    • 依赖数组控制:空数组=挂载/卸载时执行,省略=每次渲染执行
    useEffect(() => {
      const subscription = props.source.subscribe();
      return () => subscription.unsubscribe(); // 清理函数
    }, [props.source]); // 依赖项变化时重新订阅
    
  3. useRef - 持久化引用

    • 跨渲染周期保存值(不触发重渲染)
    • 访问DOM节点的官方方式
    const inputRef = useRef();
    useEffect(() => { inputRef.current.focus() }, []);
    return <input ref={inputRef} />;
    

三、进阶Hooks应用场景

  1. 性能优化三剑客

    • useMemo:缓存计算结果
    • useCallback:缓存回调函数
    • React.memo:组件浅比较缓存
    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
    const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);
    
  2. 全局状态管理

    • useContext + useReducer实现轻量Redux

    const ThemeContext = createContext('light');
    function App() {
      return (
        <ThemeContext.Provider value="dark">
          <Toolbar />
        </ThemeContext.Provider>
      );
    }
    function Toolbar() {
      const theme = useContext(ThemeContext); // 读取当前主题
    }
    
  3. 自定义Hook实现逻辑复用

    • 提取公共逻辑(如监听窗口大小、表单处理)
    function useWindowWidth() {
      const [width, setWidth] = useState(window.innerWidth);
      useEffect(() => {
        const handleResize = () => setWidth(window.innerWidth);
        window.addEventListener('resize', handleResize);
        return () => window.removeEventListener('resize', handleResize);
      }, []);
      return width;
    }
    // 使用自定义Hook
    const width = useWindowWidth();
    

四、Hooks实战最佳实践

  1. 遵循官方规则

    • 只在函数组件顶层调用Hooks(不在循环/条件中)
    • 自定义Hook必须以use开头(便于ESLint规则检查)
  2. 调试与性能分析

    • 使用React DevTools的Hooks调试功能
    • 通过<Profiler>组件测量渲染性能
  3. 测试策略

    • 使用@testing-library/react-hooks单独测试自定义Hooks
    test('should use custom hook', () => {
      const { result } = renderHook(() => useCounter())
      act(() => result.current.increment())
      expect(result.current.count).toBe(1)
    })
    

五、Hooks生态与未来趋势

  1. 主流库的Hooks适配

    • React Router:useParams, useNavigate
    • Redux:useSelector, useDispatch
    • SWR/React Query:useSWR, useQuery
  2. Server Components新范式

    • 服务端组件与客户端组件的Hooks使用差异
    • Streaming SSR与Hooks的结合