type
Post
status
Published
date
Jun 22, 2026
slug
reacrApi
summary
React 中几个非常常见的 API:createRoot、StrictMode、组件、Props、useState、useEffect、useCallback、useRef,以及 TypeScript 在 React 中的基本写法。
tags
笔记
React
category
前端开发
icon
password
类型
日期
标签
概述
状态
进行中
从 GitHub 用户搜索项目学习 React 常用 API
项目路径:
这个项目的核心功能很简单:页面打开时自动请求 octocat 用户信息,用户也可以输入 GitHub 用户名,点击搜索或按下回车后重新请求 GitHub API,并把用户信息展示出来。
一、React 应用入口:createRoot
项目入口在 src/main.tsx:
这里最重要的是:
createRoot 的作用是把 React 应用挂载到 HTML 页面中的某个 DOM 节点上。
通常 index.html 里会有:
React 并不是直接接管整个 HTML 文件,而是把组件渲染到 #root 这个节点里面。
document.getElementById("root")! 里的 ! 是 TypeScript 的非空断言,意思是告诉 TypeScript:“这里一定能找到 root 元素”。
二、StrictMode:开发阶段的提醒工具
入口文件中还用了:
StrictMode 是 React 提供的开发辅助组件。它不会在页面上渲染额外内容,主要作用是在开发环境中帮助发现一些潜在问题。
比如在 React 18 的开发环境里,useEffect 可能会执行两次,这是 React 故意这样做的,用来帮助开发者发现副作用逻辑是否安全。
在这个项目中,useGithubUser 里使用了 AbortController 取消请求,这其实就很适合配合 StrictMode。因为当组件重复挂载、卸载时,请求不会乱掉。
三、组件:把页面拆成可维护的小模块
App.tsx 很简单:
真正的页面逻辑放在 GithubPage 中:
项目把页面拆成了几个组件:
这样的拆分方式很适合初学 React:
- GithubPage 负责状态和业务逻辑
- GithubSearchForm 负责搜索输入框
- GithubStatusPanel 负责显示当前状态
- GithubFeedback 负责显示 loading 和 error
- GithubUserCard 负责显示用户信息
这体现了 React 的核心思想:页面不是一大坨 HTML,而是由多个组件组合出来的 UI。
四、useState:管理组件状态
在 GithubPage.tsx 中:
useState 用来声明组件内部状态。
这里的 username 表示输入框当前的值,初始值是 "octocat"。
当输入框变化时:
然后把它传给搜索表单组件:
输入框组件内部使用这个状态:
这就是 React 里的“受控组件”。
输入框的值不再由 DOM 自己管理,而是由 React 的 username 状态管理。用户输入时触发 onChange,再通过 setUsername 更新状态,页面随之重新渲染。
五、Props:父组件给子组件传数据
GithubSearchForm 的 Props 类型定义如下:
Props 可以理解为父组件传给子组件的参数。
比如父组件 GithubPage 把 username、loading 和几个事件处理函数传给 GithubSearchForm:
子组件只负责使用:
这样做的好处是:子组件不需要知道 GitHub API 怎么请求,也不需要知道状态怎么维护,它只负责渲染和触发事件。
这就是 React 中非常重要的设计方式:状态上移,组件下发 Props。
六、条件渲染:根据状态显示不同内容
项目中有很多条件渲染。
例如 GithubFeedback.tsx:
这里的意思是:
- 如果 error 有内容,就显示错误信息
- 如果 loading 是 true,就显示加载提示
再比如 GithubPage.tsx 中:
只有当 user 存在时,才显示用户卡片。
这是 React 中很常见的写法。页面显示什么,不是直接操作 DOM,而是通过状态决定 JSX 渲染结果。
七、自定义 Hook:把可复用逻辑抽出去
这个项目里最值得学习的是 useGithubUser.ts。
它是一个自定义 Hook:
自定义 Hook 本质上就是一个函数,只不过它内部可以使用 React 的 Hook,比如 useState、useEffect、useCallback、useRef。
这个 Hook 把 GitHub 用户请求相关的逻辑都封装起来:
- 用户数据 user
- 加载状态 loading
- 错误信息 error
- 请求函数 fetchGithubUser
页面组件只需要这样使用:
这比把所有请求逻辑都写在页面组件里更清晰。
八、useEffect:组件加载后自动请求数据
在 useGithubUser 中:
useEffect 用来处理副作用。
什么是副作用?简单说,就是 React 渲染之外的事情,比如:
- 请求接口
- 操作 DOM
- 设置定时器
- 订阅事件
- 清理资源
这里的作用是:组件第一次加载时,自动请求默认用户 octocat。
后面的 return 是清理函数:
当组件卸载时,如果还有未完成的请求,就取消它,避免请求结束后再更新已经卸载的组件。
九、useCallback:缓存函数引用
请求函数使用了 useCallback:
useCallback 的作用是缓存函数引用。
如果不使用 useCallback,组件每次渲染时都会创建一个新的 fetchGithubUser 函数。
这里为什么需要它?
因为 useEffect 依赖了 fetchGithubUser:
如果 fetchGithubUser 每次渲染都是新函数,useEffect 就可能被反复触发。
使用 useCallback 后,只要依赖项不变,React 就会复用之前的函数。
十、useRef:保存不会触发渲染的数据
在 useGithubUser 中:
useRef 可以保存一个可变值,而且修改它不会触发组件重新渲染。
这里用它保存当前请求的 AbortController。
当用户连续搜索多个用户名时,前一次请求可能还没结束。如果不处理,就可能出现旧请求覆盖新请求的问题。
项目里这样处理:
每次发起新请求前,先取消上一次请求,然后创建新的 AbortController。
这是一种很实用的请求控制方式。
十一、API 请求层:把 fetch 封装起来
项目没有把 fetch 直接写在组件里,而是放到了 services/githubApi.ts:
这样做有几个好处:
第一,组件不需要关心 API 地址。
第二,错误处理可以集中管理。
第三,接口返回的数据可以先格式化,再交给页面使用。
GitHub API 返回的数据字段是下划线风格:
但前端组件更习惯使用驼峰风格:
所以项目中有一个转换函数:
这也是一个很好的工程习惯:不要让接口原始数据直接污染组件层。
十二、TypeScript
项目中定义了 GitHub 用户类型:
这里区分了两个类型:
- GithubApiUser:接口原始返回数据
- GithubUser:前端组件真正使用的数据
这让代码更清楚。
比如用户卡片组件:
组件接收的 user 必须符合 GithubUser 类型。
所以在组件里可以放心使用:
?? 是空值合并运算符。比如 user.name 是 null 时,就显示 user.login。
十三、搜索流程
用户点击搜索按钮时:
触发 GithubPage 里的:
然后进入自定义 Hook:
整个流程可以理解为:
- 用户输入 GitHub 用户名
- useState 保存输入框内容
- 点击搜索按钮
- 调用 fetchGithubUser
- 设置 loading 为 true
- 请求 GitHub API
- 成功后设置 user
- 失败后设置 error
- 页面根据最新状态重新渲染
React 的核心其实就是这句话:数据变了,页面自动重新渲染。
总结
这个 GitHubSearch 项目虽然功能不复杂,但已经包含了 React 开发中非常核心的一组能力:
- 组件化开发
- 状态管理
- Props 通信
- 表单受控
- 条件渲染
- 副作用处理
- 自定义 Hook
- API 请求封装
- 请求取消
- TypeScript 类型建模
React 的学习重点不是把每个 API 的定义背下来,而是理解它们分别解决什么问题。
在这个项目中:
- useState 解决“数据会变化”的问题
- useEffect 解决“组件加载后做事情”的问题
- useCallback 解决“函数引用稳定”的问题
- useRef 解决“保存可变对象但不触发渲染”的问题
- 自定义 Hook 解决“业务逻辑复用和拆分”的问题
- Props 解决“父子组件通信”的问题
- Author:Uonlra
- URL:https://www.uonlra.blog//article/reacrApi
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!









