【免费下载链接】howtographql
The Fullstack Tutorial for GraphQL
本篇指南来自 howtographql 仓库的 React + urql 前端教程(Hackernews Clone 项目),核心讲解如何用 urql 以声明式方式执行 GraphQL 查询:从编写最简单的Link/LinkListReact 组件开始,到用gql定义feed查询、通过useQuery钩子发送请求,最终把服务端数据渲染到页面并正确处理加载中(fetching)与错误(error)状态。读完本篇,你将掌握 urql 查询体系的完整调用链(Client → Exchanges → Hooks),能够独立在自己的 React 应用中接入任意 GraphQL API。
本讲在教程中的位置
本篇对应仓库中的 2-queries-loading-links.md,是 React + urql 教程的第 2 章。在此之前,1-getting-started.md 已经完成了三件准备工作:
- 用
create-react-app创建了hackernews-react-urql前端项目,并建立了src/components与src/styles目录结构; - 安装了
urql、@urql/exchange-graphcache、graphql、graphql-tag四个依赖; - 在
src/index.js中配置了 urql 的Client(指向http://localhost:4000),并用Provider注入到整个 React 组件树;后端方面则下载了 Node 教程的server目录,部署了 Prisma 数据库服务,通过 Playground 创建了两条 Link 记录。
本讲要做的,就是让前端真正"开口"向这个 GraphQL API 要数据,并把数据渲染出来。整个过程中你会看到 urql 最核心的设计:操作(Operation)经由 Exchanges 中间件链处理,Hooks 只是 Client 的便捷封装。
第一步:准备 React 组件
本讲第一个功能是加载并展示一组Link元素。按照组件自底向上的顺序,先编写渲染单个链接的组件。
在src/components目录新建Link.js:
import React from 'react' const Link = ({ link }) => ( <div> <div> {link.description} ({link.url}) </div> </div> ) export default Link这是一个极简组件:从props中接收一个link对象,渲染其description与url两个字段。教程特意使用两层<div>嵌套——不追求语义化 HTML,纯粹为后续章节的 Tachyons 样式预留挂载点。
接下来编写渲染链接列表的LinkList组件,同样放在src/components目录下:
import React from 'react' import Link from './Link' const linksToRender = [ { id: '1', description: 'Prisma turns your database into a GraphQL API 😎', url: 'https://www.prismagraphql.com', }, { id: '2', description: 'The best GraphQL client', url: 'https://formidable.com/open-source/urql/', }, ] const LinkList = () => ( <div> {linksToRender.map(link => <Link key={link.id} link={link} />)} </div> ) export default LinkList这里先硬编码了一份linksToRender模拟数据,用于验证组件链路是否通畅——稍后会用服务端真实数据替换它。注意key={link.id}是 React 列表渲染的必备属性。
最后修改src/components/App.js,让应用渲染LinkList:
import React from 'react' import LinkList from './LinkList' const App = () => <LinkList /> export default App运行yarn start,浏览器打开http://localhost:3000,页面应显示linksToRender数组中的两条链接,说明组件树搭建成功。
第二步:编写 GraphQL 查询
接下来要让应用读取数据库中真实的 Link 数据。首先需要定义要发送的 GraphQL 查询:
{ feed { links { id createdAt description url } } }这份查询针对的是应用层 schema(application schema)。回顾 1-getting-started.md 中展示的server/src/schema.graphql,feed查询的定义如下:
type Query { feed(filter: String, skip: Int, first: Int, orderBy: LinkOrderByInput): Feed! } type Feed { links: [Link!]! count: Int! }也就是说,feed返回一个Feed对象,其links字段是非空数组,每个元素包含id、createdAt、description、url等字段;它同时还支持filter、skip、first、orderBy参数(本讲暂不使用,后续分页与过滤章节会用到)。查询可以先用 GraphQL Playground(后端yarn start后在http://localhost:4000打开)直接执行验证,再接入前端。
第三步:理解 urql 的查询机制
用 urql 发送查询有两条路径:底层命令式 API 与 React 声明式 Hooks。理解这两层关系,是掌握 urql 的关键。
底层 API:executeQuery 与 Wonka 流
urql 的 React 绑定内部会调用Client上的方法,这些方法返回一个"结果流"(stream of results)。三个核心方法是executeQuery、executeMutation、executeSubscription,返回的结果流基于 Wonka 库实现。直接使用它们比 React 绑定略啰嗦,但能看清底层机制:
import { createRequest } from 'urql' import { pipe, subscribe } from 'wonka' const request = createRequest(gql` { feed { links { id } } } `, { // ... variables }); pipe( client.executeQuery(request), subscribe(response => { console.log(response.data.feed); }) );流程是:用createRequest把「查询文档 + 变量」打包成请求对象 → 交给client.executeQuery得到 Wonka 流 → 通过pipe+subscribe订阅每个结果。
声明式 API:三个 Hooks
对 React 开发者而言,更常用的是 urql 的 Hook API。根据操作类型,urql 提供三个对应的 Hooks 以及带 render-prop 的同名组件:
useQuery—— 查询useMutation—— 变更useSubscription—— 订阅
这些 Hooks 是 urqlClient的便捷封装:它们自动处理请求取消、结果更新、初始状态设置。向useQuery传入query选项(可选传入variables),Hook 内部会通知 Client 执行查询,缓存(cache)则能在数据变化或缓存失效时主动向组件推送更新。
urql 官方的设计理念是:核心功能全部通过 Exchanges 实现。回顾 1-getting-started.md 中的 Client 配置:
import { Provider, Client, dedupExchange, fetchExchange } from 'urql' import { cacheExchange } from '@urql/exchange-graphcache' const cache = cacheExchange({}) const client = new Client({ url: 'http://localhost:4000', exchanges: [dedupExchange, cache, fetchExchange], })这里手动组装了三个 Exchanges:dedupExchange(对同一时刻的相同查询去重)、cacheExchange(来自@urql/exchange-graphcache的规范化缓存,替代默认的文档缓存)、fetchExchange(用fetch发送请求并支持取消)。顺序有讲究——dedup在最前、fetch在最后。你在组件里调用useQuery时,请求会依次经过这条链,最终由fetchExchange发往http://localhost:4000。
概括而言,用 urql 添加数据获取逻辑通常遵循同一套三步流程:
- 用
gql解析函数把查询写成 JavaScript 常量; - 调用
useQuery钩子,传入{ query, variables }; - 解构钩子返回的结果,
const [result] = useQuery(...)。
第四步:把查询接入 LinkList 组件
先打开LinkList.js,在文件顶部定义查询常量:
const FEED_QUERY = gql` { feed { links { id createdAt url description } } } `然后改造LinkList组件,调用useQuery:
const LinkList = () => { useQuery({ query: FEED_QUERY }); return ( <div> {linksToRender.map(link => <Link key={link.id} link={link} />)} </div> ); };这里发生了两件事:
FEED_QUERY用gql函数解析包含 GraphQL 代码的字符串(反引号语法是 JavaScript 的标签模板字符串,graphql-tag依赖的作用就在于此);- 在组件中调用
useQuery,把FEED_QUERY传给query选项。
注意:示例仍然返回linksToRender模拟数据——此时还只是"发起了请求",尚未消费useQuery的结果。
为了让代码运行,还需要在文件顶部补齐两行导入:
import { useQuery } from 'urql' import gql from 'graphql-tag'到这一步,全部"取数代码"就完成了。刷新应用,可以看到已经有请求发往 GraphQL API(可在浏览器 Network 面板验证),但页面仍显示模拟数据。
第五步:渲染服务端真实数据
现在删除linksToRender模拟数据,改为消费useQuery的返回结果:
const LinkList = () => { const [result] = useQuery({ query: FEED_QUERY }) const { data, fetching, error } = result if (fetching) return <div>Fetching</div> if (error) return <div>Error</div> const linksToRender = data.feed.links return ( <div> {linksToRender.map(link => <Link key={link.id} link={link} />)} </div> ) }逐一拆解这段代码:
useQuery返回一个数组,第一个元素是result。之所以是数组而非对象,是因为 urql 所有 Hooks 的第二个值永远是execute函数(可用于手动重新执行查询/变更);result的三个核心属性完整描述了查询状态:
| 属性 | 含义 |
|---|---|
fetching | 请求进行中、响应尚未到达时为true,否则为false |
error | 请求失败时包含一个CombinedError对象,说明具体出错原因;根据错误类型,它带有networkError(网络层错误)或graphQLErrors(GraphQL 层错误)属性 |
data | 服务端返回的实际数据;由于FEED_QUERY请求了links字段,这里会有links属性,值为Link元素列表 |
- 渲染前用
fetching/error做守卫,分别渲染 "Fetching" 与 "Error" 占位内容,这是 GraphQL 前端应用的常见模式; - 数据就绪后
data.feed.links即为链接数组,与之前模拟数据形状一致,Link组件无需任何改动。
完成后刷新页面,http://localhost:3000上应显示与之前完全相同的两条链接——只是数据来源从本地数组换成了 GraphQL API(即 1-getting-started.md 中通过 Playground 创建的 "The best GraphQL client" 与 "Prisma turns your database into a GraphQL API" 两条记录)。
运行与排错要点
如果浏览器访问http://localhost:4000只显示 error 且页面空白,多半是后端没有运行。注意这个应用需要两个进程同时运行:一个 GraphQL 服务器(http://localhost:4000),一个 React 应用(http://localhost:3000)。启动服务器的方法是进入server目录执行yarn start:
cd server yarn start小结与下一步
本讲完成了两件事:
- 创建了
Link与LinkList组件; - 通过
useQuery钩子从 GraphQL API 加载 feed 数据并渲染。
同时你已理解 urql 查询的完整链路:useQuery(Hooks 封装)→Client.executeQuery(操作执行)→ Exchanges 链(去重、规范化缓存、fetch 发送)→ 返回结果流,最终以fetching/error/data三个状态驱动 UI。
后续章节将沿着同一条路径继续深入:发送变更用 3-mutations-creating-links.md 的useMutation;路由与重定向见 4-routing.md;关于useQuery的requestPolicy(cache-first、cache-only、network-only、cache-and-network)以及缓存更新机制,可参考 7-pagination-and-cache-updates.md。feed查询的完整能力(过滤、分页、排序)则对应后端教程的 8-filtering-pagination-and-sorting.md。
【免费下载链接】howtographql
The Fullstack Tutorial for GraphQL
相关推荐
HowToGraphQL React + Apollo 实战:用 useQuery 钩子通过 GraphQL 查询加载并渲染链接列表
HowToGraphQL React + Apollo 实战:用 useQuery 钩子通过 GraphQL 查询加载并渲染链接列表 本篇指南基于 HowToG
在 Preact 中使用 urql:@urql/preact 的安装、Provider 配置与 useQuery 组合式 GraphQL 数据流实战
在 Preact 中使用 urql:@urql/preact 的安装、Provider 配置与 useQuery 组合式 GraphQL 数据流实战 urql
前端urql 实战:在 React 中增量渲染 `@defer` 与 `@stream` 指令的延迟/流式 GraphQL 响应
urql 实战:在 React 中增量渲染 @defer 与 @stream 指令的延迟/流式 GraphQL 响应 本篇指南以仓库 examples/with
前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考