【react】TypeScript在react中的使用

目录

一、环境与项目配置

1. 创建 TypeScript React 项目

2. 关键tsconfig.json配置

3.安装核心类型包

二、组件类型定义

 1. 函数组件(React 18+)

2.类组件

三、Hooks 的深度类型集成

 1. useState

 2. useEffect

 3. useRef

4. 自定义 Hook

四、事件处理

1. 表单事件

2. 鼠标/键盘事件

五、状态管理(Redux Toolkit)

1.定义类型化的 Slice

2. 类型化的useSelector和useDispatch(组件中使用)

六、路由(React Router v6)

七、类型扩展与最佳实践

1. 扩展全局类型

2. 组件默认 Props

3. 泛型组件

4.高阶组件(HOC)类型

5.Context API类型安全

八、测试与调试

1. Jest + Testing Library

2.类型安全的Mock数据

九、常见问题

1. 如何处理第三方库类型?

2.处理动态导入(Lazy Loading )

3. 类型断言的使用

3. 处理可选 Props

4、性能优化

1.使用react.memo 优化渲染

2.类型化的useCallback和useMemo

十一、学习资源


一、环境与项目配置

1. 创建 TypeScript React 项目

# 使用 Create React App(推荐)

npx create-react-app my-app --template typescript

# 或使用 Vite(更轻量)

npm create vite@latest my-react-app -- --template react-ts
2. 关键tsconfig.json配置
{
  "compilerOptions": {
    "target": "ESNext",
    "lib": ["DOM", "DOM.Iterable", "ESNext"], // 浏览器环境支持
    "jsx": "react-jsx",                      // React 17+ 的 JSX 转换
    "strict": true,                          // 启用所有严格类型检查
    "esModuleInterop": true,                  // 兼容 CommonJS/ES6 模块
    "skipLibCheck": true,                     // 跳过第三方库类型检查(提升速度)
    "forceConsistentCasingInFileNames": true, // 强制文件名大小写一致
    "baseUrl": "./src",                       // 路径别名基础目录
    "paths": {
      "@components/*": ["components/*"]       // 路径别名配置
    }
  },
  "include": ["src/**/*"]
}
3.安装核心类型包
npm install @types/react @types/react-dom @types/node --save-dev

二、组件类型定义

 1. 函数组件(React 18+)
// Button.tsx
import React from 'react';

// 定义 Props 类型
interface ButtonProps {
  children: React.ReactNode;
  onClick: () => void;
  variant?: 'primary' | 'secondary';//可选属性
  disabled?: boolean;//可选回调函数
}

//使用React.FC 泛型定义组件( React 18 后 FC 不再隐式包含 children)
const Button: React.FC<ButtonProps> = ({ 
  children, 
  onClick, 
  variant = 'primary', //默认值
  disabled = false 
}) => {
  return (
    <button 
      className={`btn-${variant}`}
      onClick={onClick}
      disabled={disabled}
    >
      {children}
    </button>
  );
};

export default Button;
2.类组件
// Counter.tsx
import React from 'react';

//State和Props类型定义
interface CounterProps {
  initialCount?: number;
}

interface CounterState {
  count: number;
}

class Counter extends React.Component<CounterProps, CounterState> {
 // 默认 Props
  static defaultProps: Partial<CounterProps> = {
    initialCount: 0
  };

  // 初始化 State(需明确类型)
  state: CounterState = {
    count: this.props.initialCount!
  };

  // 箭头函数绑定 this(避免手动 bind)
  increment = () => {
    this.setState((prev) => ({ count: prev.count + 1 }));
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

三、Hooks 的深度类型集成

 1. useState
//react
//const [count, setCount] = React.useState<number>(0); // 显式指定类型
//const [user, setUser] = React.useState<User | null>(null); // 联合类型


// 基础类型推断
const [count, setCount] = useState<number>(0);

// 复杂对象类型(明确初始值)
interface User {
  id: string;
  name: string;
  email?: string;
}

const [user, setUser] = useState<User>({
  id: '1',
  name: 'Alice'
});
 2. useEffect
// 异步请求的类型安全处理
useEffect(() => {
  let isMounted = true; // 防止组件卸载后更新状态

  const fetchData = async () => {
    try {
      const response = await fetch('/api/users');
      const data: User[] = await response.json();
      if (isMounted) setUsers(data);
    } catch (error) {
      console.error('Fetch error:', error);
    }
  };

  fetchData();

  return () => {
    isMounted = false;
  };
}, []);
 3. useRef
// DOM 引用
const inputRef = useRef<HTMLInputElement>(null);
// 可变值(非 DOM)
// 引用 DOM 元素
const inputRef = useRef<HTMLInputElement>(null);

// 存储可变值(非 DOM)
interface Timer {
  id: number;
  start: () => void;
}

const timerRef = useRef<Timer | null>(null);
4. 自定义 Hook
// useLocalStorage.ts
import { useState, useEffect } from 'react';

function useLocalStorage<T>(
  key: string,
  initialValue: T
): [T, (value: T) => void] {
  const [storedValue, setStoredValue] = useState<T>(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      return initialValue;
    }
  });

  const setValue = (value: T) => {
    try {
      setStoredValue(value);
      window.localStorage.setItem(key, JSON.stringify(value));
    } catch (error) {
      console.error('LocalStorage set error:', error);
    }
  };

  useEffect(() => {
    const handleStorageChange = (e: StorageEvent) => {
      if (e.key === key) {
        setStoredValue(JSON.parse(e.newValue!));
      }
    };

    window.addEventListener('storage', handleStorageChange);
    return () => window.removeEventListener('storage', handleStorageChange);
  }, [key]);

  return [storedValue, setValue];
}

// 使用
const [theme, setTheme] = useLocalStorage<'light' | 'dark'>('theme', 'light');

四、事件处理

1. 表单事件
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  e.preventDefault();
  // 访问表单元素
  const input = e.currentTarget.elements.namedItem('username') as HTMLInputElement;
  console.log(input.value);
};
2. 鼠标/键盘事件
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
  console.log(e.clientX, e.clientY);
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
  if (e.key === 'Enter') {
    // 处理回车
  }
};

五、状态管理(Redux Toolkit)

1.定义类型化的 Slice
// features/counter/counterSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface Todo {
  id:string;
  text: string ;
  value: number;
}
interface TodosState {
  list: Todo[];
  status: 'idle' | 'loading' | 'succeeded' | 'failef';

const initialState: TodosState = {
  list: [];
  status: 'idle'
};

const counterSlice = createSlice({
  name: 'tods',
  initialState,
  reducers: {
    addTodo: (state, action: PayloadAction<{ text: string }>) => {
      const newTodo: Todo = {
        id: Date.now().toString(),
        text: action.payload.text,
        completed: false
      };
      state.list.push(newTodo);
    },
    toggleTodo: (state, action: PayloadAction<string>) => {
      const todo = state.list.find(t => t.id === action.payload);
      if (todo) todo.completed = !todo.completed;
    }
  },
  extraReducers: (builder) => {
    // 异步处理示例
    builder
      .addCase(fetchTodos.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(fetchTodos.fulfilled, (state, action: PayloadAction<Todo[]>) => {
        state.status = 'succeeded';
        state.list = action.payload;
      });
  }
});

export const {addTodo, toggleTodo} = counterSlice.actions;
export default todoSlice.reducer;
2. 类型化的useSelector和useDispatch(组件中使用)
// Counter.tsx
import { useDispatch, useSelector } from 'react-redux';
import type { RootState } from '@/app/store';
import { increment } from '@/features/counter/counterSlice';

const Counter = () => {
  const count = useSelector((state: RootState) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <span>{count}</span>
      <button onClick={() => dispatch(increment())}>+1</button>
    </div>
  );
};

六、路由(React Router v6)

// App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';

interface RouteConfig {
  path: string;
  element: React.ReactNode;
}

const routes: RouteConfig[] = [
  { path: '/', element: <HomePage /> },
  { path: '/about', element: <AboutPage /> }
];

const App = () => (
  <BrowserRouter>
    <Routes>
      {routes.map((route) => (
        <Route key={route.path} {...route} />
      ))}
    </Routes>
  </BrowserRouter>
);

七、类型扩展与最佳实践

1. 扩展全局类型
// react-app-env.d.ts
declare namespace React {
  interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
    // 扩展自定义属性
    customAttr?: string;
  }
}
2. 组件默认 Props
interface Props {
  size?: 'small' | 'medium' | 'large';
}

const MyComponent = ({ size = 'medium' }: Props) => {
  // ...
};
3. 泛型组件
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
  return (
    <div>
      {items.map((item, index) => (
        <div key={index}>{renderItem(item)}</div>
      ))}
    </div>
  );
}

// 使用
<List<{ id: string; name: string }> 
  items={users}
  renderItem={(user) => <span>{user.name}</span>}
/>
4.高阶组件(HOC)类型
type WithLoadingProps = {
  isLoading: boolean;
};

// 高阶组件:为组件添加 loading 状态
function withLoading<T extends object>(
  WrappedComponent: React.ComponentType<T>
) {
  return (props: T & WithLoadingProps) => {
    if (props.isLoading) return <div>Loading...</div>;
    return <WrappedComponent {...props} />;
  };
}

// 使用
const UserListWithLoading = withLoading(UserList);
<UserListWithLoading isLoading={true} users={[]} />;
5.Context API类型安全
// ThemeContext.tsx
import React, { createContext, useContext } from 'react';

type Theme = 'light' | 'dark';
type ThemeContextType = {
  theme: Theme;
  toggleTheme: () => void;
};

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [theme, setTheme] = useState<Theme>('light');

  const toggleTheme = () => {
    setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

// 自定义 Hook 确保 Context 存在
export const useTheme = () => {
  const context = useContext(ThemeContext);
  if (!context) throw new Error('useTheme must be used within ThemeProvider');
  return context;
};

八、测试与调试

1. Jest + Testing Library
// Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';

test('handles click event', () => {
  const handleClick = jest.fn();
  render(<Button onClick={handleClick}>Click Me</Button>);
  
  fireEvent.click(screen.getByText(/click me/i));
  expect(handleClick).toHaveBeenCalledTimes(1);
});

// 测试异步操作
test('loads user data', async () => {
  render(<UserProfile userId="1" />);
  
  await waitFor(() => {
    expect(screen.getByText('Alice')).toBeInTheDocument();
  });
});
2.类型安全的Mock数据
// mocks/user.ts
import { User } from '../types';

export const mockUser: User = {
  id: '1',
  name: 'Alice',
  email: 'alice@example.com'
};

// 测试中使用
jest.mock('../api', () => ({
  fetchUser: jest.fn().mockResolvedValue(mockUser)
}));

九、常见问题

1. 如何处理第三方库类型?
npm install @types/react-router-dom @types/lodash # 安装类型声明

# 安装社区维护的类型包
npm install @types/lodash @types/react-select --save-dev

# 临时忽略类型检查(不推荐)
// @ts-ignore
import untypedLib from 'untyped-lib';
2.处理动态导入(Lazy Loading )
// 明确组件类型
const LazyComponent = React.lazy(() => import('./LazyComponent'));

// 使用 Suspense
<Suspense fallback={<div>Loading...</div>}>
  <LazyComponent />
</Suspense>
3. 类型断言的使用
//例a
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  const value = e.target.value as string; // 明确类型

//例b 
const element = document.getElementById('root') as HTMLElement; // 安全断言
const data = response as ApiResponse; // 明确知道类型时
3. 处理可选 Props
interface AvatarProps {
  src: string;
  alt?: string; // 可选属性
  size?: number;
}

const Avatar = ({ src, alt = '', size = 40 }: AvatarProps) => (
  <img src={src} alt={alt} width={size} />
);
4、性能优化
1.使用react.memo 优化渲染
interface MemoizedComponentProps {
  data: string[];
}

const MemoizedComponent = React.memo<MemoizedComponentProps>(({ data }) => {
  // 复杂计算
  return <div>{data.join(', ')}</div>;
});
2.类型化的useCallback和useMemo
const memoizedCallback = useCallback(
  (id: string) => {
    console.log('Callback called with:', id);
  },
  [] // 依赖项数组
);

const memoizedValue = useMemo(() => {
  return computeExpensiveValue(a, b);
}, [a, b]); // 依赖项变化时重新计算

十一、学习资源

  1. React TypeScript Cheatsheet

  2. DefinitelyTyped(第三方库类型)

  3. TypeScript 官方文档

码字不易,各位大佬点点赞呗

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/978253.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

快速排序(详解)c++

快速排序(Quick Sort)&#xff0c;既然敢起这样的名字&#xff0c;说明它是常⻅排序算法中较为优秀的。事实上&#xff0c;在很多情况下&#xff0c;快排确实是效率较⾼的算法&#xff1b;c的排序是以快排为基础&#xff0c;再加上堆排和插入排序做优化实现的&#xff0c;我们这…

【工具变量】公司企业数字领导力(2004-2023年)

数据简介&#xff1a;企业数字化领导力是指在数字经济时代&#xff0c;领导者通过战略性地使用数字资产、引领组织变革&#xff0c;使企业在数字化环境中获得持续成功的能力。对于上市公司而言&#xff0c;这种领导力尤为重要&#xff0c;因为它直接关系到企业的战略方向、市场…

浅谈新能源汽车充电桩建设问题分析及解决方案

摘要&#xff1a; 在全球倡导低碳减排的大背景下&#xff0c;新能源成为热门行业在全球范围内得以开展。汽车尾气排放会在一定程度上加重温室效应&#xff0c;并且化石能源的日渐紧缺也迫切对新能源汽车发展提出新要求。现阶段的新能源汽车以电力汽车为主&#xff0c;与燃油汽…

seacmsv9报错注入

1、seacms的介绍 ​ seacms中文名&#xff1a;海洋影视管理系统。是一个采用了php5mysql架构的影视网站框架&#xff0c;因此&#xff0c;如果该框架有漏洞&#xff0c;那使用了该框架的各个网站都会有相同问题。 2、源码的分析 漏洞的部分源码如下&#xff1a; <?php …

python学习四

python运算符与表达式 表达式: Python中的表达式是一种计算结果的代码片段。它可以包 含变量、运算符、常数和函数调用,用于执行各种数学、逻辑 和功能操作 算术运算符: 比较(关系)运算符: 赋值运算符: 逻辑运算符: 位运算符: 成员运算符: 身份运算符 <

Nginx面试宝典【刷题系列】

文章目录 1、nginx是如何实现高并发的&#xff1f;2、Nginx如何处理HTTP请求&#xff1f;3、使用“反向代理服务器”的优点是什么?4、列举Nginx服务器的最佳用途。5、Nginx服务器上的Master和Worker进程分别是什么?6、什么是C10K问题?7、请陈述stub_status和sub_filter指令的…

数字可调控开关电源设计(论文+源码)

1 设计要求 在本次数字可调控开关电源设计过程中&#xff0c;对关键参数设定如下&#xff1a; &#xff08;1&#xff09;输入电压&#xff1a;DC24-26V,输出电压&#xff1a;12-24&#xff08;可调&#xff09;&#xff1b; &#xff08;2&#xff09;输出电压误差&#xf…

清华大学《AIGC发展研究3.0》

大家好&#xff0c;我是吾鳴。 AIGC已经爆火好长一段时间了&#xff0c;特别是DeepSeek的爆火&#xff0c;直接让很多之前没有体会过推理模型的人可以免费的使用上推理模型&#xff0c;同时DeepSeek产品形态也是全球首创&#xff0c;就是直接把AI的思考过程展示给你看&#xff…

模型和数据集的平台之在Hugging Face上进行模型下载、上传以及创建专属Space

模型下载 步骤&#xff1a; 注册Hugging Face平台 https://huggingface.co/ 新建一个hf_download_josn.py 文件 touch hf_download_josn.py 编写hf_download_josn.py文件 import os from huggingface_hub import hf_hub_download# 指定模型标识符 repo_id "inter…

脚本无法获取响应主体(原因:CORS Missing Allow Credentials)

背景&#xff1a; 前端的端口号8080&#xff0c;后端8000。需在前端向后端传一个参数&#xff0c;让后端访问数据库去检测此参数是否出现过。涉及跨域请求&#xff0c;一直有这个bug是404文件找不到。 在修改过程当中不小心删除了一段代码&#xff0c;出现了这个bug&#xff…

C#实现本地AI聊天功能(Deepseek R1及其他模型)。

前言 1、C#实现本地AI聊天功能 WPFOllamaSharpe实现本地聊天功能,可以选择使用Deepseek 及其他模型。 2、此程序默认你已经安装好了Ollama。 在运行前需要线安装好Ollama,如何安装请自行搜索 Ollama下载地址&#xff1a; https://ollama.org.cn Ollama模型下载地址&#xf…

Buildroot 添加自定义模块-内置文件到文件系统

目录 概述实现步骤1. 创建包目录和文件结构2. 配置 Config.in3. 定义 cp_bin_files.mk4. 添加源文件install.shmy.conf 5. 配置与编译 概述 Buildroot 是一个高度可定制和模块化的嵌入式 Linux 构建系统&#xff0c;适用于从简单到复杂的各种嵌入式项目. buildroot的源码中bui…

音视频入门基础:RTP专题(12)——RTP中的NAL Unit Type简介

一、引言 RTP封装H.264时&#xff0c;RTP对NALU Header的nal_unit_type附加了扩展含义。 由《音视频入门基础&#xff1a;H.264专题&#xff08;4&#xff09;——NALU Header&#xff1a;forbidden_zero_bit、nal_ref_idc、nal_unit_type简介》可以知道&#xff0c;nal_unit…

智慧园区后勤单位消防安全管理:安全运营和安全巡检

//智慧园区消防管理困境大曝光 智慧园区&#xff0c;听起来高大上&#xff0c;但消防管理却让人头疼不已。各消防子系统各自为政&#xff0c;像一座座孤岛&#xff0c;信息不共享、不协同。 消防设施管理分散&#xff0c;不同区域、企业的设备标准不一样&#xff0c;维护情况…

RAG(检索增强生成)原理、实现与评测方法探讨

RAG是什么&#xff1f; 看一下RAG的英文全称&#xff1a;Retrieval-Augmented Generation&#xff0c;建索、增强、生成&#xff1b;一句话串起来就是通过检索增强模型的生成&#xff0c;是的&#xff0c;这就是RAG。 RAG怎么做&#xff1f; 目前比较通用的套路是这样的&#x…

表单制作代码,登录动画背景前端模板

炫酷动效登录页 引言 在网页设计中,按钮是用户交互的重要元素之一。一个炫酷的按钮特效不仅能提升用户体验,还能为网页增添独特的视觉吸引力。今天,我们将通过CSS来实现一个“表单制作代码,登录动画背景前端模板”。该素材呈现了数据符号排版显示出人形的动画效果,新颖有…

HBuilder X安装教程(2025版)

一&#xff0c;官网下载最新包&#xff1a; 官网链接&#xff1a;HBuilderX-高效极客技巧 等待工具包&#xff0c;下载好。 二&#xff0c;安装打开工具&#xff1a; 把HBuilderX压缩包进行压缩&#xff0c;然后打开压缩后的文件夹

【算法系列】希尔排序算法

文章目录 希尔排序算法&#xff1a;一种高效的排序方法一、基本思想二、实现步骤1. 初始化增量2. 分组与排序3. 缩小增量4. 最终排序 三、代码实现四、增量序列的选择1. Shell增量序列2. Hibbard增量序列3. Sedgewick增量序列 五、时间复杂度六、总结 希尔排序算法&#xff1a;…

VMware虚拟机Mac版安装Win10系统

介绍 Windows 10是由美国微软公司开发的应用于计算机和平板电脑的操作系统&#xff0c;于2015年7月29日发布正式版。系统有生物识别技术、Cortana搜索功能、平板模式、桌面应用、多桌面、开始菜单进化、任务切换器、任务栏的微调、贴靠辅助、通知中心、命令提示符窗口升级、文…

android keystore源码分析

架构 Android Keystore API 和底层 Keymaster HAL 提供了一套基本的但足以满足需求的加密基元&#xff0c;以便使用访问受控且由硬件支持的密钥实现相关协议。 Keymaster HAL 是由原始设备制造商 (OEM) 提供的动态加载库&#xff0c;密钥库服务使用它来提供由硬件支持的加密服…