什么类型的Relay mutator配置适合插入新记录?

时间:2015-12-30 02:20:19

标签: javascript relayjs graphql

我正在深入研究GraphQL和Relay。到目前为止,对我而言,一切都相对顺利和容易理解。我有一个与AccountsTeams一起使用的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吗?

2 个答案:

答案 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个步骤来定义突变:

  1. 根据您定位的图表部分定义输入变量 - 在您的情况下,它是一个新帐户,因此您只需要新数据
  2. 根据#1命名变异 - 对您而言,AddAccountMutation
  3. 根据受突变影响的内容定义胖查询 - 对于您来说,它只是accounts上的viewer连接,但将来我确定您的架构会变得更复杂< / LI>
  4. 根据您如何将胖查询与本地图形相交来定义变异配置
  5. 定义满足#1,#3和#4
  6. 要求所需的变异片段

    一般来说,第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_DELETERANGE_ADD等。