我有一个我的GraphQL客户端请求的对象。
这是一个相当简单的对象:
type Element {
content: [ElementContent]
elementId: String
name: String
notes: String
type: String
createdAt: String
updatedAt: String
}
使用特殊类型ElementContent
,它很小,看起来像这样:
type ElementContent {
content: String
locale: String
}
现在,当我在客户端查询时,顶级对象和低级对象都有其他属性(如果我尝试按原样克隆主体,则会干扰更新对象);
值得注意的是,GraphQL似乎在父对象中提供了__typename
属性,并且在子对象中,它们也具有typename和Symbol(id)
属性。
我想将此对象复制到状态,更新状态,然后克隆状态并将其发送到我的update
突变。但是,由于GraphQL本身提供的未知属性,我遇到了障碍。
我尝试过:
delete element.__typename
效果很好,但是我还需要遍历子项(动态数组对象),并且可能还必须删除这些属性。
我不确定在这个等式中我是否遗漏了某些东西,或者我应该在代码中挣扎并循环+删除(我最初尝试执行forEach循环时收到错误)。对于我正在尝试做的事情,是否有更好的策略?或者我是在正确的道路上,只需要一些好的循环代码来清理不需要的属性?
答案 0 :(得分:2)
有三种方法
第一种方式
像这样更新客户端参数,它将忽略graphql中不需要的字段。
apollo.create({
link: http,
cache: new InMemoryCache({
addTypename: false
})
});
第二种方式
通过使用omit-deep软件包并将其用作中间件
const cleanTypeName = new ApolloLink((operation, forward) => {
if (operation.variables) {
operation.variables = omitDeep(operation.variables,'__typename')
}
return forward(operation).map((data) => {
return data;
});
});
第三种方式
创建自定义中间件并注入到阿波罗中
const cleanTypeName = new ApolloLink((operation, forward) => {
if (operation.variables) {
const omitTypename = (key, value) => (key === '__typename' ? undefined : value);
operation.variables = JSON.parse(JSON.stringify(operation.variables), omitTypename);
}
return forward(operation).map((data) => {
return data;
});
});
并注入中间件
const httpLinkWithErrorHandling = ApolloLink.from([
cleanTypeName,
retry,
error,
http,
]);
首选方法为第三种方法,因为它没有任何第三种依赖关系,也没有缓存性能问题
答案 1 :(得分:2)
如果要从GraphQL响应中清除__typename
(从根目录及其子级开始),可以使用graphql-anywhere软件包。
类似:
const wipedData = filter(inputFragment, rcvData);
inputFragment
是定义字段的片段(您可以查看详细信息here)rcvData
是从GraphQL查询接收的数据通过使用filter
函数,wipedData
仅包含您需要作为突变输入传递的必填字段。
答案 2 :(得分:0)
这就是我所做的,也支持文件上传。它是我在Github线程上找到的多个建议的合并:Feature idea: Automatically remove __typename
from mutations
import { parse, stringify } from 'flatted';
const cleanTypename = new ApolloLink((operation, forward) => {
const omitTypename = (key, value) => (key === '__typename' ? undefined : value);
if ((operation.variables && !operation.getContext().hasUpload)) {
operation.variables = parse(stringify(operation.variables), omitTypename);
}
return forward(operation);
});
简化我的client.tsx
文件的其余部分:
import { InMemoryCache } from 'apollo-cache-inmemory';
import { createUploadLink } from 'apollo-upload-client';
import { ApolloClient } from 'apollo-client';
import { setContext } from 'apollo-link-context';
import { ApolloLink } from 'apollo-link';
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem(AUTH_TOKEN);
return {
headers: {
...headers,
authorization: token ? `Bearer ${ token }` : '',
},
};
});
const httpLink = ApolloLink.from([
cleanTypename,
authLink.concat(upLoadLink),
]);
const client = new ApolloClient({
link: httpLink,
cache,
});
export default client;
现在,当我调用类型为上载的突变时,只需将上下文hasUpload
设置为true,如下所示:
UpdateStation({variables: { input: station }, context: {hasUpload: true }}).then()
答案 3 :(得分:0)
对于那些正在寻找 TypeScript 解决方案的人:
import cloneDeepWith from "lodash/cloneDeepWith";
export const omitTypenameDeep = (
variables: Record<string, unknown>
): Record<string, unknown> =>
cloneDeepWith(variables, (value) => {
if (value && value.__typename) {
const { __typename, ...valWithoutTypename } = value;
return valWithoutTypename;
}
return undefined;
});
const removeTypename = new ApolloLink((operation, forward) => {
const newOperation = operation;
newOperation.variables = omitTypenameDeep(newOperation.variables);
return forward(newOperation);
});
// ...
const client = new ApolloClient({
cache: new InMemoryCache(),
link: ApolloLink.from([removeTypename, httpLink]),
});