0

我正在尝试使用 Relay Todo 示例中的修改代码设置突变。

当我尝试编译时,出现以下错误:

-- GraphQL Validation Error -- AddCampaignMutation --

File:  /Users/me/docker/relay/examples/todo/js/mutations/AddCampaignMutation.js
Error: Cannot query field "addCampaign" on type "Mutation".
Source:
> 
> mutation AddCampaignMutation {addCampaign}
>                               ^^^

-- GraphQL Validation Error -- AddCampaignMutation --

File:  /Users/me/docker/relay/examples/todo/js/mutations/AddCampaignMutation.js
Error: Unknown type "AddCampaignPayload". Did you mean "AddTodoPayload" or "RenameTodoPayload"?
Source:
> 
> fragment AddCampaignMutationRelayQL on AddCampaignPayload @relay(pattern: true) {
>                                        ^^

我复制了 Todo 代码,所以我不知道为什么 Todo 突变工作正常,但我的新 Campaign 测试却没有。

这是我的 database.js 文件,我删除了 Todo 相关项以使文档更易于阅读:

export class Campaign {}
export class User {}

// Mock authenticated ID
const VIEWER_ID = 'me';

// Mock user data
const viewer = new User();
viewer.id = VIEWER_ID;
const usersById = {
  [VIEWER_ID]: viewer,
};

// Mock campaign data
const campaignsById = {};
const campaignIdsByUser = {
  [VIEWER_ID]: [],
};

let nextCampaignId = 0;
addCampaign('Campaign1');
addCampaign('Campaign2');
addCampaign('Campaign3');
addCampaign('Campaign4');

export function addCampaign(text) {
  const campaign = new Campaign();
  //campaign.complete = !!complete;
  campaign.id = `${nextCampaignId++}`;
  campaign.text = text;
  campaignsById[campaign.id] = campaign;
  campaignIdsByUser[VIEWER_ID].push(campaign.id);
  return campaign.id;
}

export function getCampaign(id) {
  return campaignsById[id];
}

export function getCampaigns(status = 'any') {
  const campaigns = campaignIdsByUser[VIEWER_ID].map(id => campaignsById[id]);
  if (status === 'any') {
    return campaigns;
  }
  return campaigns.filter(campaign => campaign.complete === (status === 'completed'));
}

这是我的 schema.js 文件,我再次删除了 Todo 相关项以使文档更易于阅读:

import {
  GraphQLBoolean,
  GraphQLID,
  GraphQLInt,
  GraphQLList,
  GraphQLNonNull,
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLString,
} from 'graphql';

import {
  connectionArgs,
  connectionDefinitions,
  connectionFromArray,
  cursorForObjectInConnection,
  fromGlobalId,
  globalIdField,
  mutationWithClientMutationId,
  nodeDefinitions,
  toGlobalId,
} from 'graphql-relay';

import {

  Campaign,
  addCampaign,
  getCampaign,
  getCampaigns,

  User,
  getViewer,
} from './database';

const {nodeInterface, nodeField} = nodeDefinitions(
  (globalId) => {
    const {type, id} = fromGlobalId(globalId);
    if (type === 'User') {
      return getUser(id);
    } else if (type === 'Campaign') {
      return getCampaign(id);
    }
    return null;
  },
  (obj) => {
    if (obj instanceof User) {
      return GraphQLUser;
    } else if (obj instanceof Campaign) {
      return GraphQLCampaign;
    } 
    return null;
  }
);

/**
 * Define your own connection types here
 */


const GraphQLAddCampaignMutation = mutationWithClientMutationId({
  name: 'AddCampaign',
  inputFields: {
    text: { type: new GraphQLNonNull(GraphQLString) },
  },
  outputFields: {
    campaignEdge: {
      type: GraphQLCampaignEdge,
      resolve: ({localCampaignId}) => {
        const campaign = getCampaign(localCampaignId);
        return {
          cursor: cursorForObjectInConnection(getCampaigns(), campaign),
          node: campaign,
        };
      },
    },
    viewer: {
      type: GraphQLUser,
      resolve: () => getViewer(),
    },
  },
  mutateAndGetPayload: ({text}) => {
    const localCampaignId = addCampaign(text);
    return {localCampaignId};
  },
});

const GraphQLCampaign = new GraphQLObjectType({
  name: 'Campaign',
  description: 'Campaign integrated in our starter kit',
  fields: () => ({
    id: globalIdField('Campaign'),
    text: {
      type: GraphQLString,
      description: 'Name of the campaign',
      resolve: (obj) => obj.text,   
    }
  }),
  interfaces: [nodeInterface]
});

const {
  connectionType: CampaignsConnection,
  edgeType: GraphQLCampaignEdge,
} = connectionDefinitions({
  name: 'Campaign',
  nodeType: GraphQLCampaign,
});

const GraphQLUser = new GraphQLObjectType({
  name: 'User',
  fields: {
    id: globalIdField('User'),
    campaigns: {
      type: CampaignsConnection,
      args: {
        ...connectionArgs,
      },
      resolve: (obj, {...args}) =>
        connectionFromArray(getCampaigns(), args),
    },    
    totalCount: {
      type: GraphQLInt,
      resolve: () => getTodos().length,
    },
    completedCount: {
      type: GraphQLInt,
      resolve: () => getTodos('completed').length,
    },
  },
  interfaces: [nodeInterface],
});

const Root = new GraphQLObjectType({
  name: 'Root',
  fields: {
    viewer: {
      type: GraphQLUser,
      resolve: () => getViewer(),
    },
    node: nodeField,
  },
});

这是我的 AddCampaignMutation.js 文件:

import Relay from 'react-relay';

export default class AddCampaignMutation extends Relay.Mutation {
  static fragments = {
    viewer: () => Relay.QL`
      fragment on User {
        id,
        totalCount,
      }
    `,
  };
  getMutation() {
    console.log('getMutation');  
    return Relay.QL`mutation{addCampaign}`;
  }
  getFatQuery() {
    console.log('getFatQuery');
    return Relay.QL`
      fragment on AddCampaignPayload @relay(pattern: true) {
        campaignEdge,
        viewer {
          campaigns,
        },
      }
    `;
  }
  getConfigs() {
    console.log('getConfigs');
    return [{
      type: 'RANGE_ADD',
      parentName: 'viewer',
      parentID: this.props.viewer.id,
      connectionName: 'campaigns',
      edgeName: 'campaignEdge',
      rangeBehaviors: ({orderby}) => {
        if (orderby === 'newest') {
          return 'prepend';
        } else {
          return 'append';
        }
      },      
      //rangeBehaviors: ({status}) => {
      //  if (status === 'completed') {
      //    return 'ignore';
      //  } else {
      //    return 'append';
      //  }
      //},
    }];
  }
  getVariables() {
    console.log('getVariables');  
    return {
      text: this.props.text,
    };
  }
  getOptimisticResponse() {
    console.log('getOptimisticResponse');  
    return {
      // FIXME: totalCount gets updated optimistically, but this edge does not
      // get added until the server responds
      campaignEdge: {
        node: {
          text: this.props.text,
        },
      },
      viewer: {
        id: this.props.viewer.id,
        totalCount: this.props.viewer.totalCount + 1,
      },
    };
  }
}

最后,这是包含我的文本输入和对 AddCampaignMutation 的调用的应用程序文件:

import AddTodoMutation from '../mutations/AddTodoMutation';
import AddCampaignMutation from '../mutations/AddCampaignMutation';
import TodoListFooter from './TodoListFooter';
import TodoTextInput from './TodoTextInput';

import React from 'react';
import Relay from 'react-relay';

class TodoApp extends React.Component {
  _handleTextInputSave = (text) => {
    debugger;
    this.props.relay.commitUpdate(
      new AddTodoMutation({text, viewer: this.props.viewer})
    );
  };

  _campaignHandleTextInputSave = (text) => {
    debugger;
    this.props.relay.commitUpdate(
      new AddCampaignMutation({text, viewer: this.props.viewer})
    );
  };

  render() {
    const hasTodos = this.props.viewer.totalCount > 0;
    return (
      <div>
        <section className="todoapp">
          <header className="header">
            <TodoTextInput
              autoFocus={true}
              className="new-campaign"
              onSave={this._campaignHandleTextInputSave}
              placeholder="Campaign name"
            />          
            <h1>
              todos
            </h1>
            <TodoTextInput
              autoFocus={true}
              className="new-todo"
              onSave={this._handleTextInputSave}
              placeholder="What needs to be done?"
            />
          </header>

          {this.props.children}

          {hasTodos &&
            <TodoListFooter
              todos={this.props.viewer.todos}
              viewer={this.props.viewer}
            />
          }
        </section>
      </div>
    );
  }
}

export default Relay.createContainer(TodoApp, {
  fragments: {
    viewer: () => Relay.QL`
      fragment on User {
        totalCount,
        ${AddTodoMutation.getFragment('viewer')},
        ${AddCampaignMutation.getFragment('viewer')},
        ${TodoListFooter.getFragment('viewer')},
      }
    `,
  },
});
4

1 回答 1

0

好吧,我觉得有点傻,但我发现了问题所在。我没有意识到我的 schema.json 文件没有更新!

如果有人遇到类似问题,请确保 schema.json 文件是最新的,方法是运行以下命令来重建它:

npm 运行脚本更新模式

于 2016-06-17T13:00:38.353 回答