据我所知,片段屏蔽被认为是开发 graphql 客户端时的最佳实践,但我在思考如何在这种模糊程度下编写一些简单的反应必需品时遇到了一些困难。一种常见的必要性是在迭代中提供
key
属性:
我正在研究的一个示例是从 Github 提取存储库数据以打印每张卡片。查询是:
fragment RepositoryCard on Repository {
resourcePath
description
}
然后,我会在一个更大的查询中使用此查询来请求用户配置文件并获取他们的一些存储库:
query GetUserData($login: String!) {
user(login: $login) {
bio
name
repositories(first: 10) {
edges {
node {
...RepositoryCard
}
}
}
}
}
到目前为止,一切都很好。然后我会将响应映射到卡片上:
{
data?.user?.repositories?.edges?.map((repository) => (
<RepositoryCard
className="my-2"
repositoryNode={repository?.node}
/>
))
}
但是,这次迭代我需要一个
key
道具。最好的方法是使用 resourcePath
,因为它是独一无二的。但是,由于使用了片段屏蔽,graphql-codegen
不允许我看到repository.node
类型的内容,因此我无法从组件外部访问resourcePath
来获取它。
解决这个问题的常用方法是什么?
看起来
useFragment
并不是真正的Hook,所以没有必要遵循Hook的规则。在另一个问题的回答中,我看到解决方案是简单地将该函数重命名为不会触发警告的名称(并且更好地表明它不是一个钩子),所以在你的codegen.ts
:
generates: {
"./src/__generated__/": {
preset: "client",
plugins: [],
presetConfig: {
gqlTagName: "gql",
fragmentMasking: {
unmaskFunctionName: "getFragmentData",
}
}
}
然后您可以在
getFragmentData
内调用 useFragment
(以前的 map
)而不会收到任何警告:
data?.repositories?.edges?.filter((r) => !!(r?.node))
.map((r) => r?.node)
.map((repository) => {
const key = getFragmentData(REPOSITORY_CARD_FRAGMENT, repository)!.resourcePath;
return (
<RepositoryCard
key={key}
className="my-2"
query={repository!}
/>
);
})
在撰写本文时,当前接受的答案错误地强加了您的父组件通过取消屏蔽来依赖于组件片段中的所有字段。
如果您需要依赖顶级组件中的某些属性或字段,则不应取消屏蔽底层组件片段来获取该字段。您应该在查询中显式定义该字段,表明您依赖于该字段,在本例中为
resourcePath
。
query GetUserData($login: String!) {
user(login: $login) {
bio
name
repositories(first: 10) {
edges {
node {
resourcePath
...RepositoryCard
}
}
}
}
}
{
data?.user.repositories.edges.map((repository) => (
<RepositoryCard
key={repository.node.resourcePath}
className="my-2"
repositoryNode={repository.node}
/>
))
}
在节点中显式指定字段,同时让您的
RepositoryFragment
也定义它是完全可以的,因为两者都依赖于同一个字段。由于片段屏蔽的性质,这尤其必要,这使得字段依赖性变得明确。
片段组合将为您处理字段的重复数据删除。
请参阅 GraphQL 规范中的字段选择合并。
此外,如果您的模式设置正确,则不需要使用可选链接(?。)。