在本文中,我们将使用 Apollo 客户端从以同名动画电视节目命名的Rick and Morty API中获取数据。我们将编写一个GraphQL 查询来获取我们需要的数据。然后将使用 React 显示数据。
在我们开始这个项目之前,让我们回顾一下 GraphQL 的用例,以及它与 REST API 的不同之处。
(React教程:java567.com/search.html?sWord=react&v=2306011)
GraphQL 的用例是什么?
GraphQL 用于构建需要实时数据同步的应用程序,例如聊天应用程序。它允许开发人员获取必要的数据,减少网络上的数据传输,并提高应用程序性能。
微服务处理应用程序的特定功能或特性,这对开发人员单独使用多个 API 提出了挑战。
GraphQL 允许开发人员创建单个 API,作为通往众多微服务的网关。它还提高了性能,因为一个查询在单个请求中检索各种微服务。
GraphQL 提供了一种自文档化模式,使开发人员可以轻松理解数据模型和数据之间的关系。它还简化了创建、测试和维护 API 的过程,从而减少了时间和成本。
最后,GraphQL 提供了版本控制功能,允许在不破坏现有客户端的情况下改进 API 模式。版本控制是可能的,因为客户指定了他们需要的确切数据,这使得添加新字段和删除折旧字段变得容易,而不会影响现有客户。
GraphQL 和 REST API 之间有什么区别?
使用 GraphQL,客户端发送一个带有它需要的数据的查询,服务器只用该数据响应。另一方面,使用REST API,客户端向端点发送请求,服务器使用与端点相关的所有数据/响应进行响应。
REST API 是基于资源的,其中端点表示可以访问、创建、更新或删除的数据。另一方面,GraphQL 是基于图的,其中每个节点代表对象之间的关系。
REST API,以 JSON(JavaScript 对象表示法)或 XML(可扩展标记语言)格式返回数据。同时,GraphQL 允许客户端指定他们需要的数据,并用匹配查询的 JSON 对象进行响应。
GraphQL 提供版本控制以在不中断现有客户端的情况下实现 API 的发展,而 REST API 为每个版本创建新的端点。
在某些情况下,REST API 可能会出现过度获取或获取不足的情况,服务器可能会发送过多或较少的数据。GraphQL 通过允许客户端请求他们需要的数据来解决这个问题,从而减少通过网络传输的数据量。
项目设置
现在您已经熟悉了可以使用 GraphQL 做什么,让我们开始构建项目。
先决条件
- React基础知识
- 了解API和CSS(层叠样式表)的工作原理
依赖安装
创建一个名为“rickandmorty”的新 React App。
npm init [React](https://react.dev/)-app rickandmorty
或者
npx create-[React](https://react.dev/)-app rickandmorty
安装 Apollo 客户端和 GraphQL。下面的代码安装了两个依赖项:
- @apollo/client 包含您需要的一切,例如内存缓存、本地状态管理、错误处理和基于 React 的视图层。
- GraphQL 提供解析查询的逻辑。
npm install @apollo/client [GraphQL](https://graphql.org/)
Rick & Morty API 和 Apollo 客户端设置
设置项目后,我们需要开始在我们的文件中使用它。接下来,index.js使用命令导航到您的文件cd,并添加以下代码:
import [React](https://react.dev/)DOM from '[React](https://react.dev/)-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
const client = new ApolloClient({
uri: 'https://rickandmortyapi.com/[GraphQL](https://graphql.org/)',
cache: new InMemoryCache(),
});
const root = [React](https://react.dev/)DOM.createRoot(document.getElementById('root'));
root.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
);
上面的代码使用瑞克和莫蒂 API GraphQL 端点的 URL(统一资源定位器)创建了一个 Apollo 客户端实例。
App 组件与 Apollo 提供程序组件一起包装,以将客户端传递给所有子组件。
查询实现
characters.js现在,在文件夹内创建一个名为的文件src。该文件将包含查询和您要添加的任何其他函数。
在文件中,添加以下代码:
import { gql } from '@apollo/client';
export const GET_CHARACTERS = gql`
query Characters{
characters{
results {
name
species
status
type
gender
origin{name}
location {name}
image
},
},
}
`;
在上面的代码中,我们gql从中导入@apollo/client来定义我们的查询。
我们创建变量并将其导出GET_CHARACTERS为带有大写字母的字符串。在 GraphQL 中定义查询时,大写被认为是最佳实践。使用模板文字包装字符串也被认为是最佳实践。
Javascript 中的对象是用键值对填充的集合或容器。键值对称为属性。
在我们的例子中,查询搜索瑞克和莫蒂中的角色。它返回一个具有该results属性的对象,该属性是一个字符对象数组。
每个角色都有名称、物种、状态、类型、性别和图像等属性——您可以选择要获取的内容😉。
其他属性 origin 和 location 是具有每个角色的 origin 和 location 的 name 属性的对象。
字符函数定义
在该character.js文件中,修改后的查询下方添加以下代码,GET_CHARACTERS如下所示:
import { useQuery, gql } from '@apollo/client';
import { useState } from "[React](https://react.dev/)";
import { RandomCharacter } from './randomcharacters';
import './App.css';
export const GET_CHARACTERS = gql`
query Characters($name: String){
characters ( filter: {name: $name}){
results {
name
species
status
type
gender
origin{name}
location {name}
image
},
},
}
`;
export function CharacterList() {
const [searchTerm, setSearchTerm] = useState("");
const {loading, error, data } = useQuery(GET_CHARACTERS, {variables: {name: searchTerm}});
const handleChange = (event) => {
setSearchTerm(event.target.value);
};
return (
<div>
<input type="text" name="search" placeholder="search for Rick and Morty characters..." value={searchTerm} onChange={handleChange} className="search-input" />
{loading && (
<div className="loader-container">
<div className="loader"></div>
</div>
)}
{error && <p> error </p> }
{data?.characters.results.length === 0 && (<> <RandomCharacter/> </>)}
{data && data.characters.results.map((character) => (
<div className="card" key={character.name} style={{ backgroundImage: `url(${character.image})`,backgroundRepeat: 'no-repeat'}}>
<div className="info">
<h2 className="h3"> {character.name}</h2>
<p> Status: {character.status}</p>
<p> Species: {character.species} </p>
<p> Type: {character.type}</p>
<p> Gender: {character.gender}</p>
<p> Origin: {character.origin.name}</p>
<p> Location: {character.location.name}</p>
</div>
</div>
))}
</div>
);
}
创建export function CharacterList()一个函数,该函数也导出到代码的其他部分并且可以被代码的其他部分使用。
该searchTerm变量将搜索状态初始化为空字符串,并创建一个函数setSearchTerm来更新值。
useQuery来自库的钩子从@apollo/clientAPI 获取数据。
查询传递GET_CHARACTERS一个名为searchTermwhich 的变量,该变量用于保存要搜索的字符名称。
该handleChange 变量将searchTerm的值设置为输入字段的当前值。该input字段是用户将用于搜索他们想要查看的字符的名称的搜索栏。状态由 处理handleChange。
我们还需要考虑加载站点的问题,以及可能发生的任何错误。
loading如果加载设置为 ,则使用微调器动态呈现True。如果将错误设置为 ,则会显示一条错误消息True。
当用户正在搜索一个不存在的角色时,我们想要显示一条消息和一个他们可以找到更多信息的替代角色——这就是进来的地方。我们稍后会定义它RandomCharacter。现在,让我们保持原样。
获取数据后,我们将data.characters.results数组映射到每个角色的卡片。
我们还想更改卡片的背景以代表信息所针对的角色。属性backgroundImage中的style处理图像的动态变化。其余项目在卡片上显示为文本。
如何显示数据
现在我们有一个功能可以工作,我们需要查看浏览器中显示的内容,以及我们是否可以进行查询并获取我们需要的数据。
在您的App.js文件中,添加以下代码:
function App() {
return (
<div>
<h1 style={{ textAlign: 'center' }} >Rick and Morty Characters</h1>
<CharacterList />
</div>
);
}
<CharacterList />组件显示有关我们从 API 获取的字符的信息。
如何随机化字符
请记住,我们调用了RandomCharacter组件,但尚未定义它。
randomcharacters.js创建一个调用的文件src并添加以下代码:
import { useQuery } from "@apollo/client";
import { gql } from '@apollo/client';
import { useState } from "[React](https://react.dev/)";
import './App.css';
export const GET_SINGLE_CHARACTER = gql`
query Character($id: ID!){
character (id: $id) {
name
species
status
type
gender
origin{name}
location {name}
image
},
},
`;
export const RandomCharacter = () => {
const [randomNumber, setRandomNumber] = useState(Math.floor(Math.random() * 200));
const { loading, error, data } = useQuery(GET_SINGLE_CHARACTER, {variables: {id: randomNumber } });
return (
<div>
<p className="intro" >
Sorry, we couldn't find that character 😞
<br/>
<br/>
How about this one instead? 😉 </p>
{/* {loading && <p>loading...</p>} */}
{loading && (
<div className="loader-container">
<div className="loader"></div>
</div>
)}
{error && <p> error </p> }
{data && (<>
<div className="card" key={data.character.name} style={{ backgroundImage: `url(${data.character.image})`,backgroundRepeat: 'no-repeat'}}>
<div className="info">
<h2 className="h3">{data.character.name}</h2>
<p>Status: {data.character.status}</p>
<p>Species: {data.character.species}</p>
<p>Type: {data.character.type}</p>
<p>Gender: {data.character.gender}</p>
<p>Origin: {data.character.origin.name}</p>
<p>Location: {data.character.location.name}</p>
</div>
</div>
</>
)}
</div>
);
};
我们将复制我们在文件中创建的查询characters.js,并将其重命名为GET_SINGLE_CHARACTER. 我们将寻找 s 而不是寻找名字ID。
我们寻找IDs 是因为它们是唯一的,并且我们希望在用户没有找到他们正在寻找的字符时随机选择字符。
randomNumber将状态初始化为Math.floor生成 0 到 199 之间的随机数的函数,使用该Math.random()方法并将其乘以 200。
该Math.floor函数将表达式的结果四舍五入为最接近的整数。每次randomNumber需要更新时,该 setRandomNumber函数都会将新值作为其参数并更新状态。
我们有一条消息提醒用户他们正在寻找的角色没有找到,但他们可以签出一个新角色。
加载微调器也在此组件中实现,如果出现任何问题,错误就在那里。图像和卡片类似于格式,characters.js因为我们希望与所有内容的外观保持一致。
如何设置显示样式
我们将使用 CSS 来设置卡片的外观样式,以及搜索栏和一般页面。
定义了功能和组件后,我们将向className需要设置样式的内容添加属性。
添加以下代码:
@import url(https://fonts.googleapis.com/css?family=Roboto:400,500,700);
body{
background: navajowhite;
font-family: Roboto, veranda;
padding-bottom: 4em;
}
.card{
position: relative;
width: 22em;
height: 30em;
background-size: 22em 30em;
box-shadow: 3px 3px 20px rgba(0,0,0,0.5);
margin: auto;
overflow: hidden;
margin-bottom: 2em;
}
.card *{
position: relative;
z-index: 2;
}
.card:hover .info{
bottom: -3em;
opacity: 1;
padding: 2px 1px;
background-color: navajowhite;
}
.info{
font-family: 'Droid Serif', serif;
font-size: 1.2em;
color: black;
line-height: 1.1em;
padding: 0 2em;
position: relative;
bottom: -4em;
opacity: 0;
background: transparent;
transition: opacity 0.3s, bottom 0.3s;
text-align: center;
}
/* search bar*/
input[type="text"] {
border: none;
border-radius: 10px;
background-color: #f2f2f2;
padding: 10px;
width: 500px;
margin: 0 auto;
display: block;
font-size: 16px;
font-family: 'Roboto', sans-serif;
box-shadow: 2px 2px 5px rgba(0,0,0,0.2);
margin-bottom: 2em;
}
input[type="text"]::placeholder {
color: #999;
font-style: italic;
}
/* no result */
.intro{
/* width: 10px; */
text-align: center;
margin: 0 auto;
color:black;
font-family: 'Droid Serif', serif;
font-size: 23px;
font-style: italic;
line-height: 20px;
padding-bottom: 15px;
}
/* spinner */
.loader-container {
display: flex;
justify-content: center;
align-items: center;
height: 100px;
}
.loader {
border: 8px solid #f3f3f3;
border-top: 8px solid black;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
注意事项:
- 该类
.card表示卡片的外观。 - 该类
.info表示字符的正文文本,例如物种。 - 该类
.intro表示未找到该字符时出现的文本。 - 该类
.loader表示在显示结果之前显示的微调器。
结论
在本文中,您学习了如何将 GraphQL 查询与 React 结合使用,如何使用 useState 挂钩管理状态,以及如何设置 Web 应用程序不同组件的样式。
(SQL教程:java567.com/search.html?sWord=sql&v=2306011)