React的新Hook(扩充中)

216 阅读3分钟

React 自 16.8 版本引入 Hooks 以来,逐渐成为函数组件开发的主流方式。除了经典的 useStateuseEffect,React 还推出了一些新的 Hooks,用于解决更复杂的状态管理、性能优化和副作用处理问题。以下是 React 中一些重要的新 Hooks 及其用途:


一、核心新 Hooks

1. useReducer

  • 用途:用于管理复杂的状态逻辑,类似于 Redux 的 reducer 模式。

  • 适用场景:状态逻辑复杂、需要多个子状态或依赖前一个状态的场景。

    const initialState = { count: 0 };
    
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return { count: state.count + 1 };
        case 'decrement':
          return { count: state.count - 1 };
        default:
          throw new Error();
      }
    }
    
    function Counter() {
      const [state, dispatch] = useReducer(reducer, initialState);
      return (
        <>
          Count: {state.count}
          <button onClick={() => dispatch({ type: 'increment' })}>+</button>
          <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
        </>
      );
    }
    

2. useContext

  • 用途:用于在组件树中共享状态,避免逐层传递 props。

  • 适用场景:主题切换、用户认证、多语言等全局状态管理。

    const ThemeContext = React.createContext('light');
    
    function App() {
      return (
        <ThemeContext.Provider value="dark">
          <Toolbar />
        </ThemeContext.Provider>
      );
    }
    
    function Toolbar() {
      const theme = useContext(ThemeContext);
      return <div>Current theme: {theme}</div>;
    }
    

3. useRef

  • 用途:用于保存可变值(如 DOM 引用或计时器 ID),且在组件重新渲染时不会触发更新。

  • 适用场景:访问 DOM 元素、保存上一次的值、避免重复渲染。

    function TextInput() {
      const inputRef = useRef(null);
    
      const focusInput = () => {
        inputRef.current.focus();
      };
    
      return (
        <>
          <input ref={inputRef} type="text" />
          <button onClick={focusInput}>Focus Input</button>
        </>
      );
    }
    

4. useMemo

  • 用途:用于缓存计算结果,避免在每次渲染时重复计算。

  • 适用场景:计算密集型操作、优化性能。

    function ExpensiveComponent({ a, b }) {
      const result = useMemo(() => {
        return a + b; // 仅当 a 或 b 变化时重新计算
      }, [a, b]);
    
      return <div>Result: {result}</div>;
    }
    

5. useCallback

  • 用途:用于缓存函数引用,避免在每次渲染时创建新的函数。

  • 适用场景:优化子组件性能(避免不必要的重新渲染)。

    function ParentComponent() {
      const [count, setCount] = useState(0);
    
      const handleClick = useCallback(() => {
        setCount((prev) => prev + 1);
      }, []); // 空依赖数组表示函数不会变化
    
      return <ChildComponent onClick={handleClick} />;
    }
    

二、高级新 Hooks

1. useLayoutEffect

  • 用途:与 useEffect 类似,但在浏览器完成布局和绘制之前同步执行。

  • 适用场景:需要直接操作 DOM 或测量布局的场景。

    function MeasureComponent() {
      const [width, setWidth] = useState(0);
      const ref = useRef(null);
    
      useLayoutEffect(() => {
        setWidth(ref.current.offsetWidth);
      }, []);
    
      return <div ref={ref}>Width: {width}px</div>;
    }
    

2. useImperativeHandle

  • 用途:用于自定义暴露给父组件的实例值或方法。

  • 适用场景:封装第三方库或需要暴露特定方法的场景。

    function FancyInput(props, ref) {
      const inputRef = useRef();
      useImperativeHandle(ref, () => ({
        focus: () => {
          inputRef.current.focus();
        },
      }));
      return <input ref={inputRef} />;
    }
    
    FancyInput = forwardRef(FancyInput);
    
    function Parent() {
      const inputRef = useRef();
      return (
        <>
          <FancyInput ref={inputRef} />
          <button onClick={() => inputRef.current.focus()}>Focus Input</button>
        </>
      );
    }
    

3. useDebugValue

  • 用途:用于在 React 开发者工具中显示自定义 Hook 的调试信息。

  • 适用场景:开发自定义 Hook 时提供调试支持。

    function useCustomHook() {
      const [value] = useState('Hello');
      useDebugValue(value); // 在开发者工具中显示 value
      return value;
    }
    

三、React 18 新增 Hooks

1. useId

  • 用途:生成唯一的 ID,用于关联 DOM 元素(如 <label><input>)。

  • 适用场景:表单控件、无障碍支持。

    function Checkbox() {
      const id = useId();
      return (
        <>
          <label htmlFor={id}>Do you like React?</label>
          <input id={id} type="checkbox" />
        </>
      );
    }
    

2. useSyncExternalStore

  • 用途:用于订阅外部数据源(如 Redux、Zustand)的状态。

  • 适用场景:集成第三方状态管理库。

    const store = {
      getState: () => ({ count: 0 }),
      subscribe: (listener) => {
        window.addEventListener('storage', listener);
        return () => window.removeEventListener('storage', listener);
      },
    };
    
    function Counter() {
      const state = useSyncExternalStore(store.subscribe, store.getState);
      return <div>Count: {state.count}</div>;
    }
    

3. useInsertionEffect

  • 用途:用于在 DOM 更新之前插入样式或其他元素。

  • 适用场景:动态注入 CSS 或处理布局抖动。

    function useCustomStyle(style) {
      useInsertionEffect(() => {
        const styleElement = document.createElement('style');
        styleElement.textContent = style;
        document.head.appendChild(styleElement);
        return () => document.head.removeChild(styleElement);
      }, [style]);
    }
    

四、总结

Hook用途
useReducer管理复杂状态逻辑。
useContext共享全局状态。
useRef保存可变值或 DOM 引用。
useMemo缓存计算结果,优化性能。
useCallback缓存函数引用,优化子组件性能。
useLayoutEffect同步执行副作用,操作 DOM 或测量布局。
useImperativeHandle自定义暴露给父组件的实例值或方法。
useDebugValue在开发者工具中显示自定义 Hook 的调试信息。
useId生成唯一 ID,用于表单控件或无障支持。
useSyncExternalStore订阅外部数据源的状态。
useInsertionEffect在 DOM 更新之前插入样式或处理布局抖动。

这些 Hooks 极大地扩展了 React 函数组件的能力,使其能够处理更复杂的场景,同时保持代码的简洁性和可维护性。