0%

Apollo入门引导(八):通过变更修改数据

接上篇 —— Apollo 入门引导(七):通过查询获取数据 —— 继续翻译 Apollo 的官网入门引导

学习如何使用 useMutation hook 修改数据。

Apollo 入门引导 - 目录:

  1. 介绍
  2. 构建 schema
  3. 连接数据源
  4. 编写查询解析器
  5. 编写变更解析器
  6. 连接 Apollo Studio
  7. 创建 Apollo 客户端
  8. 通过查询获取数据
  9. 通过变更修改数据
  10. 管理本地状态

完成时间:10 分钟

前一节已经向 React 应用添加了多个查询,接下来添加一个变更(mutation)。过程与查询相似,但有一些重要区别。

提醒一下,变更是 GraphQL 操作,可以修改后端数据(与查询不同)。我们服务的 schema 支持以下变更:

1
2
3
4
5
6
# 不需要复制
type Mutation {
bookTrips(launchIds: [ID]!): TripUpdateResponse!
cancelTrip(launchId: ID!): TripUpdateResponse!
login(email: String): String # login token
}

将从实现登录功能开始。请注意,此变更接受单个变量 email

支持用户登录

注意:为简单起见,我们的示例应用未使用基于密码的身份验证来实现真正的用户帐户。而是用户通过提交其 email 地址并从服务接收相应的 session token 来不安全地“登录”。

定义变更

首先,导航到src/pages/login.tsx并将其内容替换为以下内容:

1
2
3
4
5
6
7
8
9
10
11
import React from 'react'; // preserve-line
import { gql, useMutation } from '@apollo/client'; // preserve-line

import { LoginForm, Loading } from '../components'; // preserve-line
import * as LoginTypes from './__generated__/login';

export const LOGIN_USER = gql`
mutation login($email: String!) {
login(email: $email)
}
`;

LOGIN_USER 定义看起来像上一节中的查询,只是它用 mutation 代替了 query。在来自 login 的响应中收到服务生成的 token ,代表了用户的 session。

应用 useMutation hook

将使用 Apollo 客户端的 useMutation React Hook来执行LOGIN_USER 变更。与 useQuery 一样,hook 的结果提供的属性可在整个变更的执行过程中填充和呈现组件。

useQuery 不同的是,useMutation 不会在其组件呈现后立即执行其操作。该 hook 返回一个mutate function,可以在需要时(例如用户提交表单时)调用该函数以执行该变更。

将以下内容添加到 login.tsx 的底部:

1
2
3
4
5
6
7
8
9
10
11
export default function Login() {
const [login, { loading, error }] = useMutation<
LoginTypes.Login,
LoginTypes.LoginVariables
>(LOGIN_USER);

if (loading) return <Loading />;
if (error) return <p>An error occurred</p>;

return <LoginForm login={login} />;
}
  • useMutation 的结果元数据(login)中的第一个对象,就是执行变更的 mutate 函数。将此函数传给 LoginForm 组件。
  • 元数据中的第二个对象与 useQuery 返回的结果对象相似,包括操作的 loadingerror 状态以及操作的结果data

现在,每当用户提交登录表单时,就会调用 login 变更。用户的 token 存储在内存缓存中,但是我们希望该 token 可在同一浏览器中多次访问时使用。接下来让解决这个问题。

保留用户 token

在对 useMutation 的调用的参数中中,可以包含一个 onCompleted 回调函数。这样一来便可以与变更的结果数据进行交互。将使用此回调来保留用户的 token

修改 login.tsx 中的 useMutation 调用以匹配以下内容:

1
2
3
4
5
6
7
8
const [login, { loading, error }] = useMutation<
LoginTypes.Login,
LoginTypes.LoginVariables
>(LOGIN_USER, {
onCompleted({ login }) {
localStorage.setItem('token', login as string);
},
});

onCompleted 回调函数将用户的唯一 ID 和 session token 存储在 localStorage 中,因此可以在用户下次访问应用时将这些值加载到内存缓存中。将在下一章中添加该功能。

向所有请求添加 Authorization

客户端应该将用户的 token 添加到每个发送到服务的 GraphQL 操作中。这使服务可以验证用户是否有权执行他们要尝试执行的操作。

index.tsx 中,修改 ApolloClient 的构造函数,以定义每个 GraphQL 请求的默认的 headers

1
2
3
4
5
6
7
const client: ApolloClient<NormalizedCacheObject> = new ApolloClient({
cache,
uri: 'http://localhost:4000/graphql',
headers: {
authorization: localStorage.getItem('token') || '',
},
});

在解析不需要 token 的操作时(例如获取启动列表),服务可以忽略 token,因此对于客户端而言,可以在每个请求中都包含 token。

启用登录表单

已经完成了 login 变更的定义,但是还没有展示用户可以执行的表单。因为已经将用户 token 存储在本地,所以在下一节中,将使用 Apollo 客户端的本地状态 API 来增强表单的某些逻辑。


前端记事本,不定期更新,欢迎关注!


👆 全文结束,棒槌时间到 👇