GraphQL 变异
在 GraphQL 中,服务器上的数据使用 GraphQL 变异 更新。变异是读写服务器操作,它既修改后端的数据,又允许你在同一个请求中查询修改后的数据。
编写变异
GraphQL 变异看起来非常像查询,只是它使用了 mutation
关键字
mutation FeedbackLikeMutation($input: FeedbackLikeData!) {
feedback_like(data: $input) {
feedback {
id
viewer_does_like
like_count
}
}
}
- 上面的变异修改服务器数据以“点赞”指定的
Feedback
对象。 feedback_like
是一个变异根字段(或变异字段),它更新后端的数据。
- 变异分两个步骤处理:首先,更新在服务器上处理,然后执行查询。这样可以确保你只看到作为变异响应的一部分已经被更新的数据。
注意,查询的处理方式相同。外部选择在内部选择之前计算。只是按照惯例,顶层变异字段具有副作用,而其他字段往往没有。
- 变异字段(在本例中为
feedback_like
)返回一个特定的 GraphQL 类型,它公开了我们在变异响应中可以查询的数据。
- 在本例中,我们查询的是更新后的反馈对象,包括更新后的
like_count
和更新后的viewer_does_like
值,指示当前查看者是否喜欢反馈对象。
上述变异的成功响应示例可能如下所示
{
"feedback_like": {
"feedback": {
"id": "feedback-id",
"viewer_does_like": true,
"like_count": 1,
}
}
}
在 Relay 中,我们也可以使用 graphql
标签声明 GraphQL 变异
const {graphql} = require('react-relay');
const feedbackLikeMutation = graphql`
mutation FeedbackLikeMutation($input: FeedbackLikeData!) {
feedback_like(data: $input) {
feedback {
id
viewer_does_like
like_count
}
}
}
`;
- 注意,变异也可以像查询或片段一样引用 GraphQL 变量。
使用 useMutation
执行变异
为了在 Relay 中对服务器执行变异,我们可以使用 commitMutation
和 useMutation API。让我们看一下使用 useMutation
API 的示例
import type {FeedbackLikeData, LikeButtonMutation} from 'LikeButtonMutation.graphql';
const {useMutation, graphql} = require('react-relay');
function LikeButton({
feedbackId: string,
}) {
const [commitMutation, isMutationInFlight] = useMutation<LikeButtonMutation>(
graphql`
mutation LikeButtonMutation($input: FeedbackLikeData!) {
feedback_like(data: $input) {
feedback {
viewer_does_like
like_count
}
}
}
`
);
return <button
onClick={() => commitMutation({
variables: {
input: {id: feedbackId},
},
})}
disabled={isMutationInFlight}
>
Like
</button>
}
让我们分析一下这里发生了什么。
useMutation
接受包含变异的 graphql 字面量作为其唯一参数。- 它返回一个包含以下项目的元组
- 一个回调(我们称之为
commitMutation
),它接受一个UseMutationConfig
,以及 - 一个布尔值,指示变异是否正在进行。
- 一个回调(我们称之为
- 此外,
useMutation
接受一个 Flow 类型参数。与查询一样,变异的 Flow 类型从 Relay 编译器生成的文件中导出。- 如果提供了此类型,则
UseMutationConfig
也将被静态类型化。始终提供此类型是一种最佳实践。
- 如果提供了此类型,则
- 现在,当使用变异变量调用
commitMutation
时,Relay 将发出一个网络请求,在服务器上执行feedback_like
字段。在这个例子中,这将找到由变量指定的反馈,并在后端记录用户点赞了那条反馈。 - 执行完该字段后,后端将选择更新后的 Feedback 对象,并从中选择
viewer_does_like
和like_count
字段。- 由于
Feedback
类型包含id
字段,Relay 编译器会自动添加id
字段的选择。
- 由于
- 收到变异响应后,Relay 将在存储中找到一个具有匹配
id
的反馈对象,并使用新接收到的viewer_does_like
和like_count
值对其进行更新。 - 如果这些值由于变异而发生了改变,任何从反馈对象中选择这些字段的组件都会重新渲染。或者,通俗地说,任何依赖于更新数据的组件都会重新渲染。
参数 FeedbackLikeData
的类型的名称是从顶层变异字段的名称派生的,即从 feedback_like
。此类型也从生成的 graphql.js
文件中导出。
响应变异刷新组件
在前面的示例中,我们手动选择了 viewer_does_like
和 like_count
。如果这些字段的值发生变化,选择这些字段的组件将被重新渲染。
但是,通常最好传播与我们要响应变异而刷新的组件相对应的片段。这是因为组件选择的数据可能会改变。
要求开发人员了解可能影响其组件数据的 所有 变异(并将其保持最新)是 Relay 希望避免要求的全局推理的例子。
例如,我们可以将变异改写如下
mutation FeedbackLikeMutation($input: FeedbackLikeData!) {
feedback_like(data: $input) {
feedback {
...FeedbackDisplay_feedback
...FeedbackDetail_feedback
}
}
}
如果执行此变异,那么 FeedbackDisplay
和 FeedbackDetail
组件所选择的任何字段都将被重新获取,并且这些组件将保持一致的状态。
传播片段通常比在变异完成后重新获取数据更好,因为更新的数据可以在一次往返中获取。
在变异完成或出错时执行回调
我们可能希望响应变异成功或失败更新一些状态。例如,我们可能希望在变异失败时提醒用户。UseMutationConfig
对象可以包含以下字段来处理此类情况
onCompleted
,一个在变异完成时执行的回调。它传递变异响应(在片段传播边界停止)。- 传递给
onCompleted
的值是变异片段,从存储中读取,在应用更新器和声明式变异指令后。这意味着不会读取未屏蔽片段中的数据,并且可能被删除的记录(例如,由@deleteRecord
删除)也可能是 null。
- 传递给
onError
,一个在变异出错时执行的回调。它传递发生的错误。
声明式变异指令
响应变异操纵连接
Relay 通过向连接(即列表)中添加项目或从连接中删除项目来轻松响应变异。例如,你可能希望将新创建的用户追加到给定的连接。有关更多信息,请参阅 使用声明式指令。
响应变异删除项目
此外,你可能希望响应变异从存储中删除项目。为此,你需要将 @deleteRecord
指令添加到要删除的 ID。例如
mutation DeletePostMutation($input: DeletePostData!) {
delete_post(data: $input) {
deleted_post {
id @deleteRecord
}
}
}
命令式地修改本地数据
有时,你想要执行的更新比仅仅更新字段的值更复杂,不能由声明式变异指令处理。对于这种情况,UseMutationConfig
接受一个 updater
函数,它让你完全控制如何更新存储。
这将在有关 命令式地修改存储数据 的部分中详细讨论。
乐观更新
通常,我们不想等待服务器响应,然后再响应用户交互。例如,如果用户点击“点赞”按钮,我们希望立即显示受影响的评论、帖子等已被用户点赞。
更一般地说,在这些情况下,我们希望立即以乐观的方式更新存储中的数据,即假设变异将成功完成。如果变异最终没有成功,我们希望回滚该乐观更新。
乐观响应
为了实现这一点,UseMutationConfig
可以包含一个 optimisticResponse
字段。
为了使该字段具有 Flow 类型,对 useMutation
的调用必须传递一个 Flow 类型参数,并且变异必须用 @raw_response_type
指令装饰。
在前面的示例中,我们可以提供以下乐观响应
{
feedback_like: {
feedback: {
// Even though the id field is not explicitly selected, the
// compiler selected it for us
id: feedbackId,
viewer_does_like: true,
},
},
}
现在,当我们调用 commitMutation
时,这些数据将立即写入存储。存储中具有匹配 id 的项目将使用新的 viewer_does_like
值更新。任何选择了此字段的组件都会被重新渲染。
当变异成功或出错时,乐观响应将被回滚。
更新 like_count
字段需要更多的工作。为了更新它,我们还应该在组件中读取当前的点赞数。
import type {FeedbackLikeData, LikeButtonMutation} from 'LikeButtonMutation.graphql';
import type {LikeButton_feedback$fragmentType} from 'LikeButton_feedback.graphql';
const {useMutation, graphql} = require('react-relay');
function LikeButton({
feedback: LikeButton_feedback$fragmentType,
}) {
const data = useFragment(
graphql`
fragment LikeButton_feedback on Feedback {
__id
viewer_does_like @required(action: THROW)
like_count @required(action: THROW)
}
`,
feedback
);
const [commitMutation, isMutationInFlight] = useMutation<LikeButtonMutation>(
graphql`
mutation LikeButtonMutation($input: FeedbackLikeData!)
@raw_response_type {
feedback_like(data: $input) {
feedback {
viewer_does_like
like_count
}
}
}
`
);
const changeToLikeCount = data.viewer_does_like ? -1 : 1;
return <button
onClick={() => commitMutation({
variables: {
input: {id: data.__id},
},
optimisticResponse: {
feedback_like: {
feedback: {
id: data.__id,
viewer_does_like: !data.viewer_does_like,
like_count: data.like_count + changeToLikeCount,
},
},
},
})}
disabled={isMutationInFlight}
>
Like
</button>
}
你应该小心,如果乐观响应的值依赖于存储的值,并且可能有多个乐观响应影响该存储值,请考虑使用 乐观更新器。
例如,如果两个乐观响应都将点赞数增加一个,并且第一个乐观更新器被回滚,则第二个乐观更新将仍然被应用,存储中的点赞数将仍然增加两个。
乐观响应包含许多陷阱!
- 乐观响应可以包含完整查询响应的数据,即包括片段扩展的内容。这意味着,如果开发人员在乐观响应中选择了片段扩展的组件中的更多字段,这些组件在乐观更新期间可能会出现不一致或部分数据。
- 由于乐观更新的类型包含所有递归嵌套片段的内容,因此它可能非常大。在某些变异中添加
@raw_response_type
可能会降低 Relay 编译器的性能。
乐观更新器
乐观响应不足以应对所有情况。例如,我们可能希望乐观地更新我们在变异中未选择的 data。或者,我们可能希望向连接添加或删除项目(而声明式变异指令不足以满足我们的用例。)
对于这些情况,UseMutationConfig
可以包含一个 optimisticUpdater
字段,它允许开发人员以命令式和乐观的方式更新存储中的数据。这将在关于命令式更新存储数据部分中详细讨论。
更新器函数的执行顺序
通常,updater
和乐观更新的执行将按照以下顺序进行
- 如果提供了
optimisticResponse
,该数据将写入存储中。 - 如果提供了
optimisticUpdater
,Relay 将执行它并相应地更新存储。 - 如果提供了
optimisticResponse
,变异中存在的声明式变异指令将在乐观响应上处理。 - 如果变异请求成功
- 任何应用的乐观更新将被回滚。
- Relay 将服务器响应写入存储。
- 如果提供了
updater
,Relay 将执行它并相应地更新存储。服务器有效负载将作为存储中的根字段提供给updater
。 - Relay 将使用服务器响应处理任何声明式变异指令。
onCompleted
回调将被调用。
- 如果变异请求失败
- 任何应用的乐观更新将被回滚。
onError
回调将被调用。
在变异期间使 data 无效
执行变异时推荐的方法是从服务器请求影响变异的所有相关数据(作为变异主体的一部分),以便我们的本地 Relay 存储与服务器状态一致。
然而,通常来说,对于具有巨大连锁效应的变异(例如,想象“封锁用户”或“离开群组”),知道和指定所有可能受影响的数据是不可行的。
对于这些类型的变异,显式地将某些 data(或整个存储)标记为陈旧通常更简单,这样 Relay 就可以知道在下次渲染时重新获取它。为了做到这一点,您可以使用我们Data 陈旧部分中记录的数据无效化 API。
此页面是否有用?
通过以下操作帮助我们进一步改进网站 回答一些简单的问题.