news 2026/9/26 2:55:35

howtographql 实战:使用 urql 的 useQuery 在 React 中加载并渲染 GraphQL 查询数据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
howtographql 实战:使用 urql 的 useQuery 在 React 中加载并渲染 GraphQL 查询数据

【免费下载链接】howtographql

The Fullstack Tutorial for GraphQL

项目地址:https://gitcode.com/gh_mirrors/ho/howtographql
点击查看免费下载

本篇指南来自 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 已经完成了三件准备工作:

  1. 用create-react-app创建了hackernews-react-urql前端项目,并建立了src/components与src/styles目录结构;
  2. 安装了urql、@urql/exchange-graphcache、graphql、graphql-tag四个依赖;
  3. 在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 添加数据获取逻辑通常遵循同一套三步流程:

  1. 用gql解析函数把查询写成 JavaScript 常量;
  2. 调用useQuery钩子,传入{ query, variables };
  3. 解构钩子返回的结果,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> ); };

这里发生了两件事:

  1. FEED_QUERY用gql函数解析包含 GraphQL 代码的字符串(反引号语法是 JavaScript 的标签模板字符串,graphql-tag依赖的作用就在于此);
  2. 在组件中调用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

项目地址:https://gitcode.com/gh_mirrors/ho/howtographql
点击查看免费下载
上一篇:3步构建你的跨设备游戏王国:Sunshine游戏串流实战指南
下一篇:5分钟上手专业级AI换脸:roop-unleashed深度伪造工具终极指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 2:54:50

【前端问题】解决浮动元素的父容器塌陷

问题&#xff1a;浮动的子元素脱离了文档流&#xff0c;父容器无法感知它们的高度&#xff0c;高度会坍塌为 0&#xff08;或仅剩边框&#xff09;&#xff0c;页脚会错误地跑到浮动元素旁边&#xff0c;而不是在容器下方。HTML<div class"father"><div cla…

作者头像 李华
网站建设 2026/9/26 2:54:05

UE5.7插件自动编译失败排查全攻略:从LNK2019到模块依赖

如果你的工作流和我一样&#xff0c;习惯在UE5.7工程里丢一个插件&#xff0c;启动编辑器让它自动编译&#xff0c;然后趁这个空档去倒杯水&#xff0c;那你大概率经历过这样一个场景&#xff1a;水还没喝上两口&#xff0c;编辑器弹出一整片红色编译错误&#xff0c;插件加载失…

作者头像 李华
网站建设 2026/9/26 2:52:24

酒店IPTV卡顿根因排查与秒开机制:从组播转单播到长期运维

深夜11点40分&#xff0c;酒店前台电话打进机房&#xff1a;302房的客人说电视一直转圈&#xff0c;已经等了五分钟还放不出来。这已经是今晚第三次同类投诉&#xff0c;而你刚换过光猫、重启过交换机、甚至把机顶盒都换了一台&#xff0c;问题依旧。如果你经历过这种场景&…

作者头像 李华