第七节: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 的设计规则(如仅在顶层调用)。

相关推荐
weixin_382395236 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__6 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.7 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~8 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华9 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子11 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅11 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni12 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学13 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端