跳至主要内容
版本: v18.0.0

强制修改存储数据

Relay 存储中的数据可以在更新器函数内强制修改。

何时使用更新器

复杂的客户端更新

如果对本地数据的更改比简单地将网络响应写入存储更为复杂,并且无法通过声明式变异指令处理,则可能需要提供更新器函数。

客户端模式扩展

此外,由于网络响应必然不会包含在客户端模式扩展中定义的字段的数据,因此您可能希望使用更新器来初始化在客户端模式扩展中定义的数据。

其他 API 的使用

最后,有些事情只能通过更新器来实现,例如使节点失效、删除节点、查找给定字段下的所有连接等。

如果多个乐观响应修改了给定的存储值

如果两个乐观响应影响了给定的值,并且第一个乐观响应被回滚,则第二个乐观响应将保持应用。

例如,如果两个乐观响应都将故事的点赞数增加一,并且第一个乐观响应被回滚,则第二个乐观响应将保持应用。但是,它不会被重新计算,并且点赞数的值将保持增加两个。

何时**不**使用更新器

触发其他副作用

您应该使用onCompleted回调来触发其他副作用。onCompleted回调保证调用一次,但更新器和乐观更新器可能会重复调用。

各种类型的更新器函数

useMutationcommitMutation API 接受配置对象,其中可以包含optimisticUpdaterupdater字段。requestSubscriptionuseSubscription API 接受配置对象,其中可以包含updater字段。

此外,还有一个 API (commitLocalUpdate) 也接受更新器函数。它将在其他用于修改本地数据的 API部分中讨论。

乐观更新器与更新器

变异可以同时具有乐观更新器和普通更新器。乐观更新器在触发变异时执行。当该变异完成或出错时,乐观更新将被回滚。

普通更新器在变异成功完成时执行。

示例

让我们构建一个示例,其中在变异更新器中,is_new_comment字段(它是在模式扩展中定义的)在新建的 Feedback 对象上设置为true

# Feedback.graphql
extend type Feedback {
is_new_comment: Boolean
}
// CreateFeedback.js
import type {Environment} from 'react-relay';
import type {
FeedbackCreateData,
CreateFeedbackMutation,
CreateFeedbackMutation$data,
} from 'CreateFeedbackMutation.graphql';

const {commitMutation, graphql} = require('react-relay');
const {ConnectionHandler} = require('relay-runtime');

function commitCreateFeedbackMutation(
environment: Environment,
input: FeedbackCreateData,
) {
return commitMutation<FeedbackCreateData>(environment, {
mutation: graphql`
mutation CreateFeedbackMutation($input: FeedbackCreateData!) {
feedback_create(input: $input) {
feedback {
id
# Step 1: in the mutation response, spread an updatable fragment (defined below).
# This updatable fragment will select the fields that we want to update on this
# particular feedback object.
...CreateFeedback_updatable_feedback
}
}
}
`,
variables: {input},

// Step 2: define an updater
updater: (store: RecordSourceSelectorProxy, response: ?CreateCommentMutation$data) => {
// Step 3: Access and nullcheck the feedback object.
// Note that this could also have been achieved with the @required directive.
const feedbackRef = response?.feedback_create?.feedback;
if (feedbackRef == null) {
return;
}

// Step 3: call store.readUpdatableFragment
const {updatableData} = store.readUpdatableFragment(
// Step 4: Pass it a fragment literal, where the fragment contains the @updatable directive.
// This fragment selects the fields that you wish to update on the feedback object.
// In step 1, we spread this fragment in the query response.
graphql`
fragment CreateFeedback_updatable_feedback on Feedback @updatable {
is_new_comment
}
`,
// Step 5: Pass the fragment reference.
feedbackRef
);

// Step 6: Mutate the updatableData object!
updatableData.is_new_comment = true;
},
});
}

module.exports = {commit: commitCreateFeedbackMutation};

让我们提炼一下这里发生的事情。

  • updater接受两个参数:一个RecordSourceSelectorProxy和一个可选对象,该对象是读取变异响应的结果。
    • 第二个参数的类型是来自生成变异文件的$data类型的可空版本。
    • 第二个参数仅包含直接由变异参数选择的数据。换句话说,它不会包含仅通过扩展片段选择的任何字段。
  • updater在变异响应被写入存储后执行。
  • 在此示例更新器中,我们执行了三件事
    • 首先,我们在变异响应中扩展可更新片段。
    • 其次,我们通过调用readUpdatableFragment读取此片段选择的字段。这将返回一个可更新的代理对象。
    • 第三,我们更新此可更新代理上的字段。
  • 此更新器完成后,记录的更新将被写入存储,并且所有受影响的组件将重新渲染。

示例 2:响应用户交互更新数据

让我们考虑响应用户交互更新存储数据的常见情况。在点击处理程序中,让我们切换is_selected字段。此字段是在客户端模式扩展中定义的 Users 上。

# User.graphql
extend type User {
is_selected: Boolean
}
// UserSelectToggle.react.js
import type {RecordSourceSelectorProxy} from 'react-relay';
import type {UserSelectToggle_viewer$key} from 'UserSelectToggle_viewer.graphql';

const {useRelayEnvironment, commitLocalUpdate} = require('react-relay');

function UserSelectToggle({ userId, viewerRef }: {
userId: string,
viewerRef: UserSelectToggle_viewer$key,
}) {
const viewer = useFragment(graphql`
fragment UserSelectToggle_viewer on Viewer {
user(user_id: $user_id) {
id
name
is_selected
...UserSelectToggle_updatable_user
}
}
`, viewerRef);

const environment = useRelayEnvironment();

return <button
onClick={() => {
commitLocalUpdate(
environment,
(store: RecordSourceSelectorProxy) => {
const userRef = viewer.user;
if (userRef == null) {
return;
}

const {updatableData} = store.readUpdatableFragment(
graphql`
fragment UserSelectToggle_updatable_user on User @updatable {
is_selected
}
`,
userRef
);

updatableData.is_selected = !viewer?.user?.is_selected;
}
);
}}
>
{viewer?.user?.is_selected ? 'Deselect' : 'Select'} {viewer?.user?.name}
</button>
}

让我们提炼一下这里发生的事情。

  • 在点击处理程序中,我们调用commitLocalUpdate,它接受一个 Relay 环境和一个更新器函数。**与前面的示例不同,此更新器不接受第二个参数**,因为没有关联的网络有效负载。
  • 在此更新器函数中,我们通过调用store.readUpdatableFragment访问获取可更新的代理对象,并切换is_selected字段。
  • 与我们调用readUpdatableFragment的先前示例一样,这可以重写为使用readUpdatableQuery API。
注意

此示例可以使用environment.commitPayload API 重写,尽管没有类型安全性。

备用 API:readUpdatableQuery.

在前面的示例中,我们使用可更新片段访问了我们要更新其字段的记录。这也可以通过可更新的查询来实现。

如果我们知道从根(即类型为Query的对象)到要修改的记录的路径,则可以使用readUpdatableQuery API 来实现这一点。

例如,我们可以响应事件设置观察者的name字段,如下所示

// NameUpdater.react.js
function NameUpdater({ queryRef }: {
queryRef: NameUpdater_viewer$key,
}) {
const environment = useRelayEnvironment();
const data = useFragment(
graphql`
fragment NameUpdater_viewer on Viewer {
name
}
`,
queryRef
);
const [newName, setNewName] = useState(data?.viewer?.name);
const onSubmit = () => {
commitLocalUpdate(environment, store => {
const {updatableData} = store.readUpdatableQuery(
graphql`
query NameUpdaterUpdateQuery @updatable {
viewer {
name
}
}
`,
{}
);
const viewer = updatableData.viewer;
if (viewer != null) {
viewer.name = newName;
}
});
};

// etc
}
  • 此特定示例可以使用readUpdatableFragment重写。但是,您可能出于以下几个原因更喜欢readUpdatableQuery
    • 您没有准备好访问片段引用,例如,如果对commitLocalUpdate的调用与组件没有明显关联。
    • 您没有准备好访问片段,其中我们选择要修改的记录的父记录(例如,在此示例中的Query)。由于 Relay 中已知的类型漏洞,可更新的片段不能在顶层扩展。
    • 您希望在可更新的片段中使用变量。目前,可更新的片段会重用传递给查询的变量。这意味着您不能,例如,具有具有片段局部变量的可更新片段,并且多次调用readUpdatableFragment,每次传递不同的变量。

此页面对您有帮助吗?

通过以下方式帮助我们让网站变得更好 回答一些快速问题.