如何在 React 和 Typescript 中映射我的 Union 类型的 GraphQL 响应数组
Posted
技术标签:
【中文标题】如何在 React 和 Typescript 中映射我的 Union 类型的 GraphQL 响应数组【英文标题】:How do I map my Union typed GraphQL response Array in React and Typescript 【发布时间】:2022-01-06 07:13:45 【问题描述】:我正在使用 React、Typescript 和 Apollo 客户端。
在我的 React 组件中,我使用 useQuery
钩子 NODES_TYPE_ONE
或 NODES_TYPE_TWO
基于值 blockData.myType
进行查询。这很好用。
GraphQL 查询如下所示:
export const NODES_TYPE_ONE = gql`
query GetNodesOne($customKey: String!)
getNodesTypeOne(customKey: $customKey)
nodes
id
title
`;
export const NODES_TYPE_TWO = gql`
query GetNodesTwo($customKey: String!)
getNodesTypeTwo(customKey: $customKey)
nodes
id
title
`;
但是如何在GqlRes
类型中输入我的数据?
当我console.log(data);
我得到:两个不同的对象:
getNodesTypeOne
nodes[// array of objects]
和
getNodesTypeTwo
nodes[// array of objects]
我的GqlRes
输入:
export type GqlRes =
getNodesTypeOne:
nodes: NodeTeaser[];
;
;
/** @jsx jsx */
import useQuery from '@apollo/client';
import jsx from '@emotion/react';
import Slides from 'app/components';
import NODES_TYPE_ONE, NODES_TYPE_TWO from './MyBlock.gql';
import Props, GqlRes, NodesArgs from './MyBlock.types';
const MyBlock = ( data: blockData, metadata : Props) =>
const customKey = metadata.customKey;
const data = useQuery<GqlRes, NodesArgs>(
blockData.myType === 'type-one' ? NODES_TYPE_ONE : NODES_TYPE_TWO,
variables:
customKey: metadata.customKey || 0,
,
errorPolicy: 'all',
notifyOnNetworkStatusChange: true,
s-s-r: false,
);
const items =
data?.getNodesTypeOne.nodes.map((video) =>
return
id: video.uuid,
type: 'type-one',
title: title,
;
) || [];
return <Slides items=items /> : null;
;
export default MyBlock;
现在我的商品只返回getNodesTypeOne
,但我如何才能同时获得它们?
更新:
我为GqlRes
创建了一个联合类型:
type GetNodeTypeOne =
getNodesTypeOne:
nodes: Teaser[];
;
;
type GetNodeTypeTwo =
getNodesTypeTwo:
nodes: Teaser[];
;
;
export type GqlRes = GetNodeTypeOne | GetNodeTypeTwo;
但是我现在如何map
节点数组呢?
更新 2
正如@Urmzd 提到的,我尝试了另一种方法。只需使用多个 useQuery
钩子:
const MyBlock = ( data: blockData, metadata : Props) =>
const customKey = metadata.customKey;
const data: nodesOne = useQuery<NodesOneGqlRes, NodesArgs>(NODES_TYPE_ONE,
variables:
customKey: metadata.customKey || 0,
,
errorPolicy: 'all',
notifyOnNetworkStatusChange: true,
s-s-r: false,
);
const data: nodesTwo = useQuery<NodesTwoGqlRes, NodesArgs>(NODES_TYPE_TWO,
variables:
customKey: metadata.customKey || 0,
,
errorPolicy: 'all',
notifyOnNetworkStatusChange: true,
s-s-r: false,
);
const items =
data?.// How do I get my nodes in a single variable?? .map((video) =>
return
id: video.uuid,
type: 'type-one',
title: title,
;
) || [];
return <Slides items=items /> : null;
;
export default MyBlock;
但是我现在如何map
我的数据,因为我有两个不同的 GraphQL 响应?在这种情况下,最好的方法是什么?
【问题讨论】:
等等,我很困惑。为什么你有两个包含完全相同实体的实体容器? 另外,当有人回答您的问题时,您可能希望扩大话题,而不仅仅是编辑自己的帖子。 你能提供你正在做的gql
查询吗?
@Urmzd 将查询添加到我的问题中。谢谢
@Urmzd 首先尝试让我的示例正常工作。我现在如何映射我的联合节点数组?
【参考方案1】:
如果我直接理解您的代码,那么根据 blockData.myType
的值,您要么执行一个查询,要么执行另一个查询,并且您想为这个逻辑重用相同的 useQuery
钩子。如果你想要,你需要确保 GqlRes
是 union type 的 getNodesTypeOne
和 getNodesTypeTwo
。
// I don't know what NodeType is so I'm just using a string for this example
type NodeType = string
interface GetNodesTypeOne
readonly getNodesTypeOne:
readonly nodes: NodeType[]
interface GetNodesTypeTwo
readonly getNodesTypeTwo:
readonly nodes: NodeType[]
type GqlRes = GetNodesTypeOne | GetNodesTypeTwo
const resultOne:GqlRes =
getNodesTypeOne:
nodes: [ "test" ]
const resultTwo:GqlRes =
getNodesTypeTwo:
nodes: [ "test" ]
所以这将解决 TypeScript 问题。然后稍后在您的代码中执行此操作:
const items = data?.getNodesTypeOne.nodes.map(...)
由于data
可能包含getNodesTypeOne
或getNodesTypeTwo
,我们需要将其更改为其他内容。一个快速的解决方法是只选择第一个具有值的:
const nodes = "getNodesTypeOne" in data
? data?.getNodesTypeOne?.nodes
: data?.getNodesTypeTwo?.nodes
const items = nodes.map(...);
或者如果你想使用相同的条件:
const nodes = blockData.myType === 'type-one'
? (data as GetNodesTypeOne)?.getNodesTypeOne?.nodes
: (data as GetNodesTypeTwo)?.getNodesTypeTwo?.nodes
const items = nodes.map(...);
请注意,在第二个示例中,我们需要使用 type assertion 帮助 TypeScript 通过 narrowing 找出具体类型。在第一个示例中,这不是必需的,因为 TypeScript 足够聪明,可以确定第一个表达式将始终导致 GetNodesTypeOne
,而第二个表达式将始终导致 GetNodesTypeOne
。
使用两个单独的查询来回答您的第二个问题:
添加一个新变量useQueryOne
,即true
,以防我们运行查询一,false
,以防我们运行查询二。
将skip
添加到useQuery
以仅运行适当的查询。
添加一个新变量nodes
,其中包含第一个查询或第二个查询的结果(基于 useQueryOne 条件)
const useQueryOne = blockData.myType === 'type-one';
const data: nodesOne = useQuery<NodesOneGqlRes, NodesArgs>(NODES_TYPE_ONE,
variables:
customKey: metadata.customKey || 0,
,
errorPolicy: 'all',
notifyOnNetworkStatusChange: true,
s-s-r: false,
skip: !useQueryOne
);
const data: nodesTwo = useQuery<NodesTwoGqlRes, NodesArgs>(NODES_TYPE_TWO,
variables:
customKey: metadata.customKey || 0,
,
errorPolicy: 'all',
notifyOnNetworkStatusChange: true,
s-s-r: false,
skip: useQueryOne
);
const nodes = useQueryOne
? nodesOne?.getNodesTypeOne?.nodes
: nodesTwo?.getNodesTypeTwo?.nodes;
const items = (nodes || []).map(...);
【讨论】:
感谢您的回答。我更新了我的问题。 你现在知道如何映射这个联合了吗? 更新了答案:-) ok 方法很明确,谢谢。但我得到这个打字稿错误:Property 'getNodesTypesOne' does not exist on type 'GqlRes'.
。好像我无法访问属性 getNodesTypesOne
和 getNodesTypesTwo
?
@meez 不幸的是 Martin 的代码不正确,他应该使用 :
而不是 |
。此外,它不是类型安全的。您需要使用类型谓词。同样,所有这些都可以通过拆分查询来解决。【参考方案2】:
我应该注意到您的查询是重复的,因此应该重构为单个查询(除非它们只是重复的命名空间而不是值)。
无论如何,您都可以使用useLazyQuery以更安全的方式实现您想要的结果
const [invokeQuery1, loading, data, error] = useLazyQuery<>(...)
const [invokeQuery2, loading2, data2, error2] = useLazyQuery<>(...)
// Run the query every time the condition changes.
useEffect(() =>
if (condition)
invokeQuery1()
else
invokeQuery2()
, [condition])
// Return the desired conditional daa
const nodes = useMemo(() =>
return condition ? data?.getNodesTypeOne : data2?.getNodesTypeTwo
,
[condition])
这也确保不会不必要地进行计算(您可以根据events
调用您的查询,因为它们应该如此)。
-- 编辑
因为您坚持使用联合类型(并从源映射数据)。
这是一种可能的、类型安全的方法。
const isNodeTypeOne = (t: unknown): t is GetNodeTypeOne =>
return (t as GetNodeTypeOne).getNodesTypeOne !== undefined;
;
const nodes = isNodeTypeOne(data)
? data?.getNodesTypeOne
: data?.getNodesTypeTwo;
const items = nodes.map((val) =>
// Your mapping here
)
如果您有不同的节点类型,您也可以在映射中使用谓词。
【讨论】:
@meez 已更新以回答您的其他问题。 我更新了我的问题。谢谢 @meez 看我的第一个答案(useEffect 和 useMemo) @meez 这解决了你的问题吗?以上是关于如何在 React 和 Typescript 中映射我的 Union 类型的 GraphQL 响应数组的主要内容,如果未能解决你的问题,请参考以下文章
如何在 React 和 Typescript 中映射我的 Union 类型的 GraphQL 响应数组
如何在 React 和 TypeScript 项目中使用 types.d.ts 文件
如何使用 React 和 TypeScript 设置 Electron?