Relay/Graphql 在使用 this.props.relay.setVariables 时查询字段 "node" 而不是 "viewer"
Relay/Graphql querying field "node" instead of "viewer" when using this.props.relay.setVariables
我有一个 table 在初始页面加载时获取 2 个项目。这正确 returns 2 行。当我检查请求负载时,我看到以下信息:
{"query":"query CampaignQuery {
viewer {
id,
...F0
}
}
fragment F0 on User {
_campaigns3iwcB5:campaigns(first:2) {
edges {
node {
id,
account_id,
start_time
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
},
id
}","variables":{}}
然后我有一个按钮可以触发一个函数并通过 this.props.relay.setVariables 将变量设置为 return 个附加项目。
然后我收到 3 次重试错误和以下错误:
[{message: "Cannot query field "node" on type "Query".", locations: [{line: 2, column: 3}]}]
当我检查请求负载时,我注意到它正在查询 "node" 而不是像以前那样查询 "viewer"。
{"query":"query Listdata_ViewerRelayQL($id_0:ID!) {
node(id:$id_0) {
...F0
}
}
fragment F0 on User {
_campaignsgsWJ4:campaigns(after:\"CkkKFwoEbmFtZRIPGg1DYW1wYWlnbiBGb3VyEipqEXN+cmVhc29uaW5nLXVzLTAxchULEghDYW1wYWlnbhiAgICA3pCBCgwYACAA\",first:2) {
edges {
node {
id,
account_id,
start_time
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
},
id
}","variables":{"id_0":"VXNlcjo="}}
这是我的 schema.js 文件
/* @flow */
/* eslint-disable no-unused-consts, no-use-before-define */
import {
GraphQLBoolean,
GraphQLFloat,
GraphQLID,
GraphQLInt,
GraphQLList,
// GraphQLEnumType,
GraphQLNonNull,
GraphQLObjectType,
GraphQLSchema,
GraphQLString
} from 'graphql';
// const types = require('graphql').type;
// const GraphQLEnumType = types.GraphQLEnumType;
import {
connectionArgs,
connectionDefinitions,
connectionFromArray,
fromGlobalId,
globalIdField,
mutationWithClientMutationId,
nodeDefinitions
} from 'graphql-relay';
import {
User,
Feature,
getUser,
getFeature,
getFeatures,
getEventStream,
Campaign,
getCampaigns,
resolveCampaigns,
campaignById,
} from './database';
// Import loader DataLoader
import Loader from './loader';
/**
* We get the node interface and field from the Relay library.
*
* The first method defines the way we resolve an ID to its object.
* The second defines the way we resolve an object to its GraphQL type.
*/
const { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
const { type, id } = fromGlobalId(globalId);
if (type === 'User') {
return getUser(id);
} else if (type === 'Feature') {
return getFeature(id);
} else if (type === 'Campaign') {
return campaignById(id);
} else {
return null;
}
},
(obj) => {
if (obj instanceof User) {
return userType;
} else if (obj instanceof Feature) {
return featureType;
} else if (obj instanceof Campaign) {
return campaignType;
} else {
return null;
}
}
);
/**
* Define your own types here
*/
const campaignType = new GraphQLObjectType({
name: 'Campaign',
description: 'A campaign',
fields: () => ({
id: globalIdField('Campaign'),
account_id: {
type: GraphQLString,
description: 'ID of the ad account that owns this campaign',
},
adlabels: {
type: GraphQLString,
description: 'Ad Labels associated with this campaign',
},
buying_type: {
type: GraphQLString,
description: 'Buying type, possible values are: AUCTION: default, RESERVED: for reach and frequency ads',
},
can_use_spend_cap: {
type: GraphQLBoolean,
description: 'Whether the campaign can set the spend cap',
},
configured_status: {
type: GraphQLString, // GraphQLEnumType,
description: '{ACTIVE, PAUSED, DELETED, ARCHIVED}. If this status is PAUSED, all its active ad sets and ads will be paused and have an effective status CAMPAIGN_PAUSED. Prefer using \'status\' instead of this.',
},
created_time: {
type: GraphQLID, // this should be a datetime
description: 'Created Time',
},
effective_status: {
type: GraphQLString, // GraphQLEnumType,
description: 'The effective status of this campaign. {ACTIVE, PAUSED, DELETED, PENDING_REVIEW, DISAPPROVED, PREAPPROVED, PENDING_BILLING_INFO, CAMPAIGN_PAUSED, ARCHIVED, ADSET_PAUSED}',
},
name: {
type: GraphQLString,
description: 'Campaign\'s name',
},
objective: {
type: GraphQLString,
description: 'Campaign\'s objective',
},
recommendations: {
type: GraphQLString, // GraphQLList,
description: 'If there are recommendations for this campaign, this field includes them. Otherwise, this field will be null.',
},
spend_cap: {
type: GraphQLFloat,
description: 'A spend cap for the campaign, such that it will not spend more than this cap. Expressed as integer value of the subunit in your currency.',
},
start_time: {
type: GraphQLID, // this should be a datetime
description: 'Start Time',
},
status: {
type: GraphQLString,
description: '{ACTIVE, PAUSED, DELETED, ARCHIVED} If this status is PAUSED, all its active ad sets and ads will be paused and have an effective status CAMPAIGN_PAUSED. The field returns the same value as \'configured_status\', and is the suggested one to use.',
},
stop_time: {
type: GraphQLID, // this should be a datetime
description: 'Stop Time',
},
updated_time: {
type: GraphQLID, // this should be a datetime
description: 'Updated Time',
},
}),
interfaces: [nodeInterface],
});
const userType = new GraphQLObjectType({
name: 'User',
description: 'A person who uses our app',
fields: () => ({
id: globalIdField('User'),
// advertising campaigns
campaigns: {
type: campaignConnection,
description: 'list of campaigns',
args: connectionArgs,
resolve: (viewer, args, source, info) => {
return resolveCampaigns(viewer, args, source, info);
},
},
features: {
type: featureConnection,
description: 'Features available to the logged in user',
args: connectionArgs,
resolve: (_, args) => connectionFromArray(getFeatures(), args)
},
username: {
type: GraphQLString,
description: 'Users\'s username'
},
website: {
type: GraphQLString,
description: 'User\'s website'
}
}),
interfaces: [nodeInterface]
});
const featureType = new GraphQLObjectType({
name: 'Feature',
description: 'Feature integrated in our starter kit',
fields: () => ({
id: globalIdField('Feature'),
name: {
type: GraphQLString,
description: 'Name of the feature'
},
description: {
type: GraphQLString,
description: 'Description of the feature'
},
url: {
type: GraphQLString,
description: 'Url of the feature'
}
}),
interfaces: [nodeInterface]
});
/**
* Define your own connection types here
*/
const {
connectionType: featureConnection
} = connectionDefinitions({
name: 'Feature',
nodeType: featureType
});
// Campaign list ConnectionType
const {
connectionType: campaignConnection,
} = connectionDefinitions({
name: 'Campaign',
nodeType: campaignType
});
/**
* This is the type that will be the root of our query,
* and the entry point into our schema.
*/
// Setup GraphQL RootQuery
const RootQuery = new GraphQLObjectType({
name: 'Query',
description: 'Realize Root Query',
fields: () => ({
viewer: {
type: userType,
resolve: () => '1'
},
})
});
/**
* This is the type that will be the root of our mutations,
* and the entry point into performing writes in our schema.
*/
const mutationType = new GraphQLObjectType({
name: 'Mutation',
fields: () => ({
// Add your own mutations here
})
});
/**
* Finally, we construct our schema (whose starting query type is the query
* type we defined above) and export it.
*/
export default new GraphQLSchema({
query: RootQuery
// Uncomment the following after adding some mutation fields:
// mutation: mutationType
});
我遇到其他人也有类似的问题,虽然他们没有提到他们是如何解决的,但他们确实是这样说的:
The problem was in my nodeDefinitions. I wasn't loading the user id correctly or identifying the node object. Once I got those working everything worked properly
如有任何帮助,我们将不胜感激。
TL;DR;
您的根查询没有 node
字段。这就是获取更多项目失败的原因。像这样添加 node
字段:
const RootQuery = new GraphQLObjectType({
...
fields: () => ({
viewer: {
...
},
node: nodeField,
})
});
when I check the Request Payload I notice that it is querying "node" instead of "viewer" like it did previously.
Relay 第一次获取对象时,它使用常规查询。
viewer {
id,
...F0
}
现在中继知道 viewer
的全局 ID。稍后当需要获取 viewer
的更多数据时,中继使用 node
根字段直接查询该对象。
node(id:$id_0) {
...F0
}
请参阅 steveluscher 的 以了解节点定义在中继中的工作方式。
我有一个 table 在初始页面加载时获取 2 个项目。这正确 returns 2 行。当我检查请求负载时,我看到以下信息:
{"query":"query CampaignQuery {
viewer {
id,
...F0
}
}
fragment F0 on User {
_campaigns3iwcB5:campaigns(first:2) {
edges {
node {
id,
account_id,
start_time
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
},
id
}","variables":{}}
然后我有一个按钮可以触发一个函数并通过 this.props.relay.setVariables 将变量设置为 return 个附加项目。
然后我收到 3 次重试错误和以下错误:
[{message: "Cannot query field "node" on type "Query".", locations: [{line: 2, column: 3}]}]
当我检查请求负载时,我注意到它正在查询 "node" 而不是像以前那样查询 "viewer"。
{"query":"query Listdata_ViewerRelayQL($id_0:ID!) {
node(id:$id_0) {
...F0
}
}
fragment F0 on User {
_campaignsgsWJ4:campaigns(after:\"CkkKFwoEbmFtZRIPGg1DYW1wYWlnbiBGb3VyEipqEXN+cmVhc29uaW5nLXVzLTAxchULEghDYW1wYWlnbhiAgICA3pCBCgwYACAA\",first:2) {
edges {
node {
id,
account_id,
start_time
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
},
id
}","variables":{"id_0":"VXNlcjo="}}
这是我的 schema.js 文件
/* @flow */
/* eslint-disable no-unused-consts, no-use-before-define */
import {
GraphQLBoolean,
GraphQLFloat,
GraphQLID,
GraphQLInt,
GraphQLList,
// GraphQLEnumType,
GraphQLNonNull,
GraphQLObjectType,
GraphQLSchema,
GraphQLString
} from 'graphql';
// const types = require('graphql').type;
// const GraphQLEnumType = types.GraphQLEnumType;
import {
connectionArgs,
connectionDefinitions,
connectionFromArray,
fromGlobalId,
globalIdField,
mutationWithClientMutationId,
nodeDefinitions
} from 'graphql-relay';
import {
User,
Feature,
getUser,
getFeature,
getFeatures,
getEventStream,
Campaign,
getCampaigns,
resolveCampaigns,
campaignById,
} from './database';
// Import loader DataLoader
import Loader from './loader';
/**
* We get the node interface and field from the Relay library.
*
* The first method defines the way we resolve an ID to its object.
* The second defines the way we resolve an object to its GraphQL type.
*/
const { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
const { type, id } = fromGlobalId(globalId);
if (type === 'User') {
return getUser(id);
} else if (type === 'Feature') {
return getFeature(id);
} else if (type === 'Campaign') {
return campaignById(id);
} else {
return null;
}
},
(obj) => {
if (obj instanceof User) {
return userType;
} else if (obj instanceof Feature) {
return featureType;
} else if (obj instanceof Campaign) {
return campaignType;
} else {
return null;
}
}
);
/**
* Define your own types here
*/
const campaignType = new GraphQLObjectType({
name: 'Campaign',
description: 'A campaign',
fields: () => ({
id: globalIdField('Campaign'),
account_id: {
type: GraphQLString,
description: 'ID of the ad account that owns this campaign',
},
adlabels: {
type: GraphQLString,
description: 'Ad Labels associated with this campaign',
},
buying_type: {
type: GraphQLString,
description: 'Buying type, possible values are: AUCTION: default, RESERVED: for reach and frequency ads',
},
can_use_spend_cap: {
type: GraphQLBoolean,
description: 'Whether the campaign can set the spend cap',
},
configured_status: {
type: GraphQLString, // GraphQLEnumType,
description: '{ACTIVE, PAUSED, DELETED, ARCHIVED}. If this status is PAUSED, all its active ad sets and ads will be paused and have an effective status CAMPAIGN_PAUSED. Prefer using \'status\' instead of this.',
},
created_time: {
type: GraphQLID, // this should be a datetime
description: 'Created Time',
},
effective_status: {
type: GraphQLString, // GraphQLEnumType,
description: 'The effective status of this campaign. {ACTIVE, PAUSED, DELETED, PENDING_REVIEW, DISAPPROVED, PREAPPROVED, PENDING_BILLING_INFO, CAMPAIGN_PAUSED, ARCHIVED, ADSET_PAUSED}',
},
name: {
type: GraphQLString,
description: 'Campaign\'s name',
},
objective: {
type: GraphQLString,
description: 'Campaign\'s objective',
},
recommendations: {
type: GraphQLString, // GraphQLList,
description: 'If there are recommendations for this campaign, this field includes them. Otherwise, this field will be null.',
},
spend_cap: {
type: GraphQLFloat,
description: 'A spend cap for the campaign, such that it will not spend more than this cap. Expressed as integer value of the subunit in your currency.',
},
start_time: {
type: GraphQLID, // this should be a datetime
description: 'Start Time',
},
status: {
type: GraphQLString,
description: '{ACTIVE, PAUSED, DELETED, ARCHIVED} If this status is PAUSED, all its active ad sets and ads will be paused and have an effective status CAMPAIGN_PAUSED. The field returns the same value as \'configured_status\', and is the suggested one to use.',
},
stop_time: {
type: GraphQLID, // this should be a datetime
description: 'Stop Time',
},
updated_time: {
type: GraphQLID, // this should be a datetime
description: 'Updated Time',
},
}),
interfaces: [nodeInterface],
});
const userType = new GraphQLObjectType({
name: 'User',
description: 'A person who uses our app',
fields: () => ({
id: globalIdField('User'),
// advertising campaigns
campaigns: {
type: campaignConnection,
description: 'list of campaigns',
args: connectionArgs,
resolve: (viewer, args, source, info) => {
return resolveCampaigns(viewer, args, source, info);
},
},
features: {
type: featureConnection,
description: 'Features available to the logged in user',
args: connectionArgs,
resolve: (_, args) => connectionFromArray(getFeatures(), args)
},
username: {
type: GraphQLString,
description: 'Users\'s username'
},
website: {
type: GraphQLString,
description: 'User\'s website'
}
}),
interfaces: [nodeInterface]
});
const featureType = new GraphQLObjectType({
name: 'Feature',
description: 'Feature integrated in our starter kit',
fields: () => ({
id: globalIdField('Feature'),
name: {
type: GraphQLString,
description: 'Name of the feature'
},
description: {
type: GraphQLString,
description: 'Description of the feature'
},
url: {
type: GraphQLString,
description: 'Url of the feature'
}
}),
interfaces: [nodeInterface]
});
/**
* Define your own connection types here
*/
const {
connectionType: featureConnection
} = connectionDefinitions({
name: 'Feature',
nodeType: featureType
});
// Campaign list ConnectionType
const {
connectionType: campaignConnection,
} = connectionDefinitions({
name: 'Campaign',
nodeType: campaignType
});
/**
* This is the type that will be the root of our query,
* and the entry point into our schema.
*/
// Setup GraphQL RootQuery
const RootQuery = new GraphQLObjectType({
name: 'Query',
description: 'Realize Root Query',
fields: () => ({
viewer: {
type: userType,
resolve: () => '1'
},
})
});
/**
* This is the type that will be the root of our mutations,
* and the entry point into performing writes in our schema.
*/
const mutationType = new GraphQLObjectType({
name: 'Mutation',
fields: () => ({
// Add your own mutations here
})
});
/**
* Finally, we construct our schema (whose starting query type is the query
* type we defined above) and export it.
*/
export default new GraphQLSchema({
query: RootQuery
// Uncomment the following after adding some mutation fields:
// mutation: mutationType
});
我遇到其他人也有类似的问题,虽然他们没有提到他们是如何解决的,但他们确实是这样说的:
The problem was in my nodeDefinitions. I wasn't loading the user id correctly or identifying the node object. Once I got those working everything worked properly
如有任何帮助,我们将不胜感激。
TL;DR;
您的根查询没有 node
字段。这就是获取更多项目失败的原因。像这样添加 node
字段:
const RootQuery = new GraphQLObjectType({
...
fields: () => ({
viewer: {
...
},
node: nodeField,
})
});
when I check the Request Payload I notice that it is querying "node" instead of "viewer" like it did previously.
Relay 第一次获取对象时,它使用常规查询。
viewer {
id,
...F0
}
现在中继知道 viewer
的全局 ID。稍后当需要获取 viewer
的更多数据时,中继使用 node
根字段直接查询该对象。
node(id:$id_0) {
...F0
}
请参阅 steveluscher 的