2016-06-23 29 views
1

私は単純なアプリケーションにもっと多くの機能をロードしようとしています。 SequelizeとGraphQLを使ってMySQLからデータを取得します。私は私のアプリケーションの足場として、リレースターターキットを使用しています。リレースローエラー:未定義のプロパティ 'reduce'を読み取ることができません

database.js

import Sequelize from 'sequelize'; 
import _ from 'lodash'; 
import Faker from 'faker'; 

const Conn = new Sequelize('relay', 'root', '', { 
    host: 'localhost', 
    dialect: 'mysql' 
}); 

const Person = Conn.define('person', { 
    firstName: { 
    type: Sequelize.STRING, 
    allowNull: false 
    }, 
    lastName: { 
    type: Sequelize.STRING, 
    allowNull: false 
    }, 
    email: { 
    type: Sequelize.STRING, 
    allowNull: false, 
    validate: { 
     isEmail: true 
    } 
    }, 
    createdAt: { 
    type: Sequelize.DATE, 
    allowNull: false, 
    }, 
    updatedAt: { 
    type: Sequelize.DATE, 
    allowNull: false, 
    } 
}); 

const Post = Conn.define('post', { 
    title: { 
    type: Sequelize.STRING, 
    allowNull: false 
    }, 
    content: { 
    type: Sequelize.STRING, 
    allowNull: false 
    } 
}); 

// Relationships 
Person.hasMany(Post); 
Post.belongsTo(Person); 

Conn.sync({ force: true }).then(() => { 
    _.times(10,() => { 
    return Person.create({ 
     firstName: Faker.name.firstName(), 
     lastName: Faker.name.lastName(), 
     email: Faker.internet.email(), 
     createdAt: Date.now(), 
     updatedAt: Date.now() 
    }).then(person => { 
     return person.createPost({ 
     title: `Sample title by ${person.firstName}`, 
     content: 'This is a sample article' 
     }); 
    }); 
    }) 
}); 

// Model types 
class User { } 

// Mock data 
var viewer = new User(); 
viewer.id = '1'; 

module.exports = { 
    // Export methods that your schema can use to interact with your database 
    getUser: (id) => id === viewer.id ? viewer : null, 
    getViewer:() => viewer, 
    Conn 
}; 

schema.js

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

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

import { 
    // Import methods that your schema can use to interact with your database 
    getUser, 
    getViewer, 
    Conn 
} from './database'; 

/** 
* 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. 
*/ 
var {nodeInterface, nodeField} = nodeDefinitions(
    (globalId) => { 
    var {type, id} = fromGlobalId(globalId); 
    if (type === 'User') { 
     return getUser(id); 
    } else { 
     return null; 
    } 
    }, 
    (obj) => { 
    if (obj instanceof User) { 
     return userType; 
    } else { 
     return null; 
    } 
    } 
); 

/** 
* Define your own types here 
*/ 

const Person = new GraphQLObjectType({ 
    name: "Person", 
    description: "This represents a person", 
    fields:() => { 
    return { 
     id: { 
     type: GraphQLString, 
     resolve(person) { 
      return person.id 
     } 
     }, 
     firstName: { 
     type: GraphQLString, 
     resolve(person) { 
      return person.firstName 
     } 
     }, 
     lastName: { 
     type: GraphQLString, 
     resolve(person) { 
      return person.lastName 
     } 
     }, 
     email: { 
     type: GraphQLString, 
     resolve(person) { 
      return person.email 
     } 
     }, 
     posts: { 
     type: new GraphQLList(Post), 
     resolve(person) { 
      return person.getPosts(); 
     } 
     } 
    } 
    } 
}); 

const Post = new GraphQLObjectType({ 
    name: "Post", 
    description: "This is a post", 
    fields:() => { 
    return { 
     id: { 
     type: GraphQLString, 
     resolve(person) { 
      return person.id; 
     } 
     }, 
     title: { 
     type: GraphQLString, 
     resolve(post) { 
      return post.title; 
     } 
     }, 
     content: { 
     type: GraphQLString, 
     resolve(post) { 
      return post.content; 
     } 
     }, 
     person: { 
     type: Person, 
     resolve(post) { 
      return post.getPerson(); 
     } 
     } 
    } 
    } 
}) 

var userType = new GraphQLObjectType({ 
    name: 'User', 
    description: 'A person who uses our app', 
    fields:() => ({ 
    id: globalIdField('User'), 
    people: { 
     type: personConnection, 
     description: 'A collection of persons', 
     args: connectionArgs, 
     resolve(_, args, info) { 
     console.log("==== INFO ===="); 
     console.log(info); 
     let gas = {}; 
     gas.first= args.first; 
     gas.after = args.after; 
     gas.find = args.find; 
     delete args.first; 
     delete args.after; 
     delete args.find; 
     return connectionFromPromisedArray(Conn.models.person.findAll({ where: args }), gas); 
     } 
    }, 
    }) 
}); 

var {connectionType: personConnection} = 
    connectionDefinitions({ name: 'Person', nodeType: Person }); 

/** 
* This is the type that will be the root of our query, 
* and the entry point into our schema. 
*/ 
var queryType = new GraphQLObjectType({ 
    name: 'Query', 
    description: "This is a root query", 
    fields:() => ({ 
    node: nodeField, 
    // Add your own root fields here 
    viewer: { 
     type: userType, 
     resolve:() => getViewer(), 
    } 
    }), 
}); 

/** 
* This is the type that will be the root of our mutations, 
* and the entry point into performing writes in our schema. 
*/ 
var 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 var Schema = new GraphQLSchema({ 
    query: queryType, 
    types: [queryType, userType, Person, Post] 
    // Uncomment the following after adding some mutation fields: 
    // mutation: mutationType 
}); 

App.js

import React from 'react'; 
import Relay from 'react-relay'; 
import AnimateOnChange from 'react-animate-on-change'; 

class Detail extends React.Component { 
    constructor(props) { 
    super(props); 
    this.state = { person: [] }; 
    } 
    componentWillMount() { 
    this.setState({ person: this.props.person }); 
    } 
    render() { 
    return (
     <div> 
     <h2>Selected User's Detail</h2> 
     <div className="form-group"> 
      <label>User ID:</label> 
      <p className="form-control"> 
      <AnimateOnChange 
       baseClassName="animated" 
       animationClassName="fadeIn" 
       animate={true}>{this.props.person.id}</AnimateOnChange> 
      </p> 
     </div> 
     <div className="form-group"> 
      <label>First Name</label> 
      <p className="form-control"> 
      <AnimateOnChange 
       baseClassName="animated" 
       animationClassName="fadeIn" 
       animate={true}>{this.props.person.firstName}</AnimateOnChange> 
      </p> 
     </div> 
     <div className="form-group"> 
      <label>Last Name</label> 
      <p className="form-control"> 
      <AnimateOnChange 
       baseClassName="animated" 
       animationClassName="fadeIn" 
       animate={true}>{this.props.person.lastName}</AnimateOnChange> 
      </p> 
     </div> 
     <h2>Selected User's Posts</h2> 
     <table className="table table-hovered"> 
      <thead> 
      <tr> 
       <th>No</th> 
       <th>Post ID</th> 
       <th>Title</th> 
       <th>Content</th> 
      </tr> 
      </thead> 
      <tbody> 
      {this.props.person.posts.map((post, key) => 
       <tr key={post.id} className="animated fadeIn"> 
       <td>{key + 1}.</td> 
       <td>{post.id} {post[key]}</td> 
       <td>{post.title}</td> 
       <td>{post.content}</td> 
       </tr> 
      ) } 
      </tbody> 
     </table> 
     </div> 
    ) 
    } 
} 

class App extends React.Component { 
    constructor(props) { 
    super(props); 
    this.state = { index: 0 } 
    } 
    _handleClick(key) { 
    if (this.state.index !== key) { 
     this.setState({ index: key }) 
    } 
    } 
    _loadMore(){ 
    console.log(this.props.viewer.people); 
    this.props.relay.setVariables({ 
     first: this.props.relay.variables.first, 
     after: this.props.viewer.people.pageInfo.endCursor 
    }); 
    } 
    render() { 
    console.log(this.props.viewer.people); 
    return (
     <div className="container"> 
     <h2>Users List</h2> 
     <button onClick={this._loadMore.bind(this)}>Load more</button> 
     <ul className="list-group"> 
      {this.props.viewer.people.edges.map((p, key) => 
      <li className={key !== this.state.index ? "list-group-item" : "list-group-item active"} onClick={this._handleClick.bind(this, key) } key={p.node.id}>{p.node.firstName} {p.node.lastName} (ID: {p.node.id}) </li> 
     ) } 
     </ul> 
     <Detail person={this.props.viewer.people.edges[this.state.index].node}/> 
     </div> 
    ) 
    } 
} 

export default Relay.createContainer(App, { 
    fragments: { 
    viewer:() => Relay.QL` 
     fragment on User { 
     people(first: $first, after: $after){ 
      edges{ 
      node{ 
       id, 
       firstName, 
       lastName, 
       posts{ 
       id, 
       title, 
       content 
       }    
      } 
      } 
      pageInfo{ 
      hasNextPage 
      hasPreviousPage 
      startCursor 
      endCursor 
      } 
     } 
     } 
    ` 
    }, 
    initialVariables: { 
    first: 3, 
    after: null 
    } 
}); 

AppHomeRoute.js

import Relay from 'react-relay'; 

export default class extends Relay.Route { 
    static queries = { 
    viewer: (Component) => Relay.QL` 
     query { 
     viewer { 
      ${Component.getFragment('viewer')} 
     } 
     } 
    `, 
    }; 
    static routeName = 'AppHomeRoute'; 
} 

このアプリケーションは単純です。ボタンをクリックすると、「もっと負荷をかける」機能がトリガーされます。 App.jsでその機能を見ることができます。関数が実行されるたびに、返されるのは:

Cannot read property 'reduce' of undefinedです。

私は、質問が正しいことを確認しようとしました。私はGraphiQLを使用してクエリを作成しようとしたときに「興味深い」ものを見つけました。

(サーバーへの要求を行う際に、アプリケーションの方法と同じ)私はフラグメントを使用する場合:

query App_ViewerRelayQL($id_0: ID!) { 
    node(id: $id_0) { 
    id 
    ...F0 
    } 
} 

fragment F0 on User { 
    _people35G0DJ: people(after: "YXJyYXljb25uZWN0aW9uOjI=", first: 2) { 
    edges { 
     node { 
     id 
     firstName 
     lastName 
     posts { 
      id 
      title 
      content 
     } 
     } 
     cursor 
    } 
    pageInfo { 
     hasNextPage 
     hasPreviousPage 
    } 
    } 
    id 
} 

結果

{ 
    "errors": [ 
    { 
     "message": "Cannot read property 'reduce' of undefined" 
    } 
    ] 
} 

私はフラグメントを使用していない場合(生のクエリ):

query { 
    viewer { 
    id 
    people(after:"YXJyYXljb25uZWN0aW9uOjE=",first:2) { 
     edges { 
     node { 
      id 
      firstName 
      lastName 
      posts { 
      id 
      title 
      content 
      } 
     } 
     cursor 
     } 
     pageInfo { 
     hasNextPage 
     hasPreviousPage 
     } 
    } 
    id 
    } 
} 

結果

{ 
    "data": { 
    "viewer": { 
     "id": "VXNlcjox", 
     "people": { 
     "edges": [ 
      { 
      "node": { 
       "id": "3", 
       "firstName": "Filomena", 
       "lastName": "Ebert", 
       "posts": [ 
       { 
        "id": "3", 
        "title": "Sample title by Filomena", 
        "content": "This is a sample article" 
       } 
       ] 
      }, 
      "cursor": "YXJyYXljb25uZWN0aW9uOjI=" 
      }, 
      { 
      "node": { 
       "id": "4", 
       "firstName": "Alessandra", 
       "lastName": "Muller", 
       "posts": [ 
       { 
        "id": "4", 
        "title": "Sample title by Alessandra", 
        "content": "This is a sample article" 
       } 
       ] 
      }, 
      "cursor": "YXJyYXljb25uZWN0aW9uOjM=" 
      } 
     ], 
     "pageInfo": { 
      "hasNextPage": true, 
      "hasPreviousPage": false 
     } 
     } 
    } 
    } 
} 

が問題であることを仮定は、クエリであるが、私はよく分かりません。私は今、少し不満を抱いています。どんな助けもありがとう。

ありがとうございました。

答えて

0

これらの変数を適切に設定しているかどうかを確認するために、_loadMore関数をもう少しデバッグしようとします。

_loadMore(){ 
    console.log(this.props.viewer.people); 
    this.props.relay.setVariables({ 
    first: this.props.relay.variables.first, 
    after: this.props.viewer.people.pageInfo.endCursor 
    }); 
} 

クエリ全体のインライン化とフラグメントの使用の唯一の違いは、これらのクエリ変数を動的に渡していることです。だから、initialVariablesのように、afterをnullに設定していると、GraphQLサーバー側でクエリが壊れている可能性があります。

関連する問題