第七节:React HooksReact 18+新特性-并发模式(Concurrent Mode)解决了什么问题?

• 考点:可中断渲染、优先级调度、startTransition使用场景

• 示例:搜索框输入防抖优化

React Hooks 进阶:自定义 Hook 设计实战指南(以 useWindowSizeuseFetch 为例)


一、自定义 Hook 设计规范

在实现 useWindowSizeuseFetch 之前,需遵循以下核心设计原则:

  1. 单一职责原则:每个 Hook 仅解决一个特定问题(如窗口尺寸监听或数据请求)。
  2. 命名规范 :以 use 开头,采用驼峰命名法(如 useWindowSize)。
  3. 参数与返回值明确:函数签名需清晰描述输入/输出,支持 TypeScript 类型定义。
  4. 副作用管理:妥善处理事件监听、异步请求等副作用,确保组件卸载时清理资源。
  5. 可配置性 :通过参数暴露配置选项(如 useFetch 的请求 URL 和防抖时间)。

二、useWindowSize 实现与优化
1. 基础实现
tsx 复制代码
import { useState, useEffect } from 'react';

type WindowSize = { width: number; height: number };

const useWindowSize = (): WindowSize => {
  const [size, setSize] = useState<WindowSize>({
    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 App() {
  const { width, height } = useWindowSize();
  return <div>窗口尺寸:{width}px × {height}px</div>;
}

关键点

• 初始值直接读取 window.innerWidth/Height,避免首次渲染时状态为 undefined

• 空依赖数组确保仅挂载时注册事件监听器,避免重复绑定。

2. 进阶优化

SSR 兼容 :通过条件判断避免服务端渲染时访问 window 对象:

tsx 复制代码
const [size, setSize] = useState<WindowSize>(() => ({
  width: typeof window !== 'undefined' ? window.innerWidth : 0,
  height: typeof window !== 'undefined' ? window.innerHeight : 0,
}));

防抖处理:添加防抖函数减少高频触发:

tsx 复制代码
useEffect(() => {
  const debouncedResize = debounce(handleResize, 200);
  window.addEventListener('resize', debouncedResize);
  return () => window.removeEventListener('resize', debouncedResize);
}, []);

三、useFetch 实现与扩展
1. 基础实现(支持 TypeScript 泛型)
tsx 复制代码
import { useState, useEffect } from 'react';

type FetchState<T> = {
  data: T | null;
  loading: boolean;
  error: Error | null;
};

const useFetch = <T,>(url: string): FetchState<T> => {
  const [state, setState] = useState<FetchState<T>>({
    data: null,
    loading: true,
    error: null,
  });

  useEffect(() => {
    const controller = new AbortController();
    
    const fetchData = async () => {
      try {
        const response = await fetch(url, { signal: controller.signal });
        if (!response.ok) throw new Error(`HTTP Error ${response.status}`);
        const data = await response.json() as T;
        setState({ data, loading: false, error: null });
      } catch (error) {
        if (!controller.signal.aborted) {
          setState({ data: null, loading: false, error: error as Error });
        }
      }
    };

    fetchData();
    return () => controller.abort();
  }, [url]);

  return state;
};

// 使用示例
interface User { id: number; name: string; }
const UserList = () => {
  const { data, loading, error } = useFetch<User[]>('/api/users');
  if (loading) return <div>加载中...</div>;
  if (error) return <div>错误:{error.message}</div>;
  return <ul>{data?.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
};

关键点

• 使用 AbortController 取消未完成的请求,避免组件卸载后更新状态。

• 泛型 <T> 支持类型推断,确保返回数据与接口定义一致。

2. 扩展功能

请求参数配置化 :支持 methodheaders 等配置项:

tsx 复制代码
type FetchOptions = RequestInit & { debounce?: number };
const useFetch = <T,>(url: string, options?: FetchOptions): FetchState<T> => {
  // 扩展配置逻辑
};

自动重试机制:添加重试次数和间隔配置:

tsx 复制代码
const fetchData = async (retryCount = 0) => {
  try {
    // 请求逻辑...
  } catch (error) {
    if (retryCount < 3) {
      setTimeout(() => fetchData(retryCount + 1), 1000);
    }
  }
};

四、自定义 Hook 最佳实践
  1. 逻辑复用与组件拆分

    • 将业务逻辑(如数据请求)与 UI 组件分离,提升可维护性。

    • 示例:useFetch 处理数据获取,<DataTable> 组件仅负责渲染。

  2. 性能优化策略

    • 避免过度渲染:在 useFetch 中通过 useMemo 缓存计算结果。

    • 请求防抖/节流:减少无效 API 调用(如搜索框输入场景)。

  3. 错误边界处理

    • 在 useFetch 中统一捕获异常,并通过返回值传递错误信息。

    • 使用 ErrorBoundary 组件捕获子组件抛出的错误。

  4. TypeScript 深度集成

    • 为自定义 Hook 添加完善的类型定义,支持泛型和类型推断。


五、总结对比
Hook 类型 核心功能 关键技术点 典型应用场景
useWindowSize 监听窗口尺寸变化 事件监听、防抖、SSR 兼容 响应式布局、图表自适应
useFetch 封装数据请求逻辑 AbortController、泛型、错误处理 表格加载、搜索接口调用

设计哲学 :通过自定义 Hook 将 React 的声明式特性与命令式逻辑结合,实现 逻辑与 UI 解耦,同时遵循 React Hooks 的设计规则(如仅在顶层调用)。

相关推荐
加班是不可能的,除非双倍日工资2 小时前
css预编译器实现星空背景图
前端·css·vue3
wyiyiyi3 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip3 小时前
vite和webpack打包结构控制
前端·javascript
excel4 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国4 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
一条上岸小咸鱼4 小时前
Kotlin 基本数据类型(三):Booleans、Characters
android·前端·kotlin
Jimmy4 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
ZXT4 小时前
promise & async await总结
前端
Jerry说前后端4 小时前
RecyclerView 性能优化:从原理到实践的深度优化方案
android·前端·性能优化
画个太阳作晴天4 小时前
A12预装app
linux·服务器·前端