我正在深入研究GraphQL和Relay。到目前为止,对我而言,一切都相对顺利和容易理解。我有一个与Accounts
和Teams
一起使用的GraphQL架构。两者之间没有关系。对于帐户和团队,我已经为连接进行了一些特定于Relay的GraphQL调整。以下是这两个连接的示例查询...
{
viewer {
teams {
edges {
node {
id,
name
}
}
}
accounts {
edges {
node {
id,
username
}
}
}
}
}
我已经准备好了一个GraphQL突变,可以创建一个新帐户。这是...的GraphQL表示...
type Mutation {
newAccount(input: NewAccountInput!): NewAccountPayload
}
input NewAccountInput {
username: String!
password: String!
clientMutationId: String!
}
type NewAccountPayload {
account: Account
clientMutationId: String!
}
type Account implements Node {
id: ID!
username: String!
date_created: String!
}
我现在正在尝试创建使用此GraphQL突变的客户端中继突变。不过,我对如何正确地做到这一点感到很困惑。我已经按照例子,我想出的任何事情似乎都没有正确运行。我倾向于得到与片段组成有关的错误。
如果我正在编写使用此GraphQL突变的Relay突变,那么相应的mutator配置是什么?我应该使用RANGE_ADD
吗?
答案 0 :(得分:6)
对于您的客户端变异,您可以使用以下内容:
export default class AddAccountMutation extends Relay.Mutation {
static fragments = {
viewer: () => Relay.QL`
fragment on Viewer {
id,
}
`,
};
getMutation() {
return Relay.QL`mutation{addAccount}`;
}
getVariables() {
return {
newAccount: this.props.newAccount,
};
}
getFatQuery() {
return Relay.QL`
fragment on AddAccountPayload {
accountEdge,
viewer {
accounts,
},
}
`;
}
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'viewer',
parentID: this.props.viewer.id,
connectionName: 'accounts',
edgeName: 'accountEdge',
rangeBehaviors: {
'': 'append',
},
}];
}
getOptimisticResponse() {
return {
accountEdge: {
node: {
userName: this.props.newAccount.userName,
},
},
viewer: {
id: this.props.viewer.id,
},
};
}
}
然后,在GraphQL架构中,您需要返回新创建的边以及光标:
var GraphQLAddAccountMutation = mutationWithClientMutationId({
name: 'AddAccount',
inputFields: {
newAccount: { type: new GraphQLNonNull(NewAccountInput) }
},
outputFields: {
accountEdge: {
type: GraphQLAccountEdge,
resolve: async ({localAccountId}) => {
var account = await getAccountById(localAccountId);
var accounts = await getAccounts();
return {
cursor: cursorForObjectInConnection(accounts, account)
node: account,
};
}
},
viewer: {
type: GraphQLViewer,
resolve: () => getViewer()
},
},
mutateAndGetPayload: async ({ newAccount }) => {
var localAccountId = await createAccount(newAccount);
return {localAccountId};
}
});
var {
connectionType: AccountsConnection,
edgeType: GraphQLAccountEdge,
} = connectionDefinitions({
name: 'Account',
nodeType: Account,
});
您需要将getAccounts(),getAccountById()和createAccount方法调用替换为您的服务器/后端使用的任何内容。
可能有一种更好的方法来计算游标而不必多次执行服务器,但请记住,Relay帮助程序cursorForObjectInConnection不会对对象进行任何深度比较,因此如果您需要通过以下方式查找帐户列表中的id,您可能需要进行自定义比较:
function getCursor(dataList, item) {
for (const i of dataList) {
if (i._id.toString() === item._id.toString()) {
let cursor = cursorForObjectInConnection(dataList, i);
return cursor;
}
}
}
最后,将GraphQL变异添加为' addAccount'到您的架构变异字段,由客户端变异引用。
答案 1 :(得分:4)
现在,我正在按照大约5个步骤来定义突变:
AddAccountMutation
accounts
上的viewer
连接,但将来我确定您的架构会变得更复杂< / LI>
一般来说,第4步是人们最容易混淆的。那是因为它令人困惑。在Stack Overflow中很难总结为什么我认为这是一个很好的建议,但是......我建议你使用FIELDS_CHANGE
来解决所有的突变*。解释和推理相对容易 - 只需告诉Relay如何查找与变异有效负载中顶级字段对应的节点。然后,中继将使用变异配置来构建一个“跟踪查询”,表示您目前所请求的所有内容,并将其与表示可能发生变化的所有内容的“胖查询”相交。在您的情况下,您希望相交的查询类似于viewer { accounts(first: 10) { edges { nodes { ... } } }
,这意味着您将要确保已经在某处已经请求了现有帐户。但你几乎肯定有,如果你还没有...也许你实际上并不需要在本地进行任何改变这个突变!
有意义吗?
编辑:为了清楚起见,这就是我对胖查询的意思&amp; CONFIGS。
getConfigs() {
return [
{
type: "FIELDS_CHANGE",
fieldIDs: {
viewer: this.props.viewer.id
}
}]
}
getFatQuery() {
return Relay.QL`
fragment on AddAccountMutation {
viewer {
accounts
}
}
`
}
*附录:我目前认为只有一两个理由不使用FIELDS_CHANGE
。首先,您无法可靠地说出哪些字段正在发生变化,因此您只想直接操作图表。第二个原因是您决定需要FIELDS_CHANGE
更具体的变体提供的查询性能优化,例如NODE_DELETE
,RANGE_ADD
等。