React with GraphQL: TypeError: parse is not a function

React with GraphQL: TypeError: parse is not a function

我目前正在尝试用对 graphql 端点的调用来替换我的 restful 调用以从服务器检索我的所有 articles。我用 GraphiQL 测试了以下查询,它 returns 预期数据。我还使用 fiddler 手动发布到端点,它按预期运行。我的问题是尝试使用 Apollo 从反应组件调用端点。

我目前收到以下错误:

TypeError: parse is not a function

从我的 graphql 查询返回的数据示例:

{
  "data": {
    "articles": [
      {
        "id": 1,
        "description": "desc1"
      },
      {
        "id": 2,
        "description": "desc2"
      }
    ]
  }
}

完整的组件:

import React, { Component } from 'react';
import { Glyphicon } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap';
import { Query } from "react-apollo";
import gql from "graphql-tag";

export class ArticlesIndex extends Component {
  displayName = ArticlesIndex.name

  constructor(props) {
    super(props);
    this.state = { articles: [], loading: true };
  }

  componentDidMount () {
      <Query
        query={gql`
        query GetArticleData {
          articles {
            id
            description
          }
        }
        `}
      >
    {({ loading, error, data }) => {
      if (loading) return <p>Loading...</p>;
      if (error) return <p>Error :(</p>;

        console.log(data.articles);
        this.setState({articles: data.articles, loading: false});
    }}
      </Query>
  }

  renderArticlesTable = articles => {
    return (
      <table className='table'>
        <thead>
          <tr>
            <th>Id</th>
            <th>Title</th>
            <th>Description</th>
            <th>Edit</th>
            <th>Delete</th>
          </tr>
        </thead>
        <tbody>
          {articles.map(article =>
            <tr key={article.id}>
              <td>{article.id}</td>
              <td>{article.title}</td>
              <td dangerouslySetInnerHTML={{ __html: article.description }}></td>
              <td>
                <LinkContainer to={'/articles/edit/' + article.id}>
                    <Glyphicon glyph='edit' />
                </LinkContainer>
            </td>
            <td onClick={(e) => this.deleteArticle(e, article.id) }>
              <Glyphicon glyph='trash' />
            </td>
            </tr>
          )}
        </tbody>
      </table>
    );
  }

  render() {
    let contents = this.state.loading
      ? <p><em>Loading...</em></p>
      : this.renderArticlesTable(this.state.articles);

    return (
      <div>
        <h1>Articles</h1>
        <LinkContainer to={'/articles/create'}>
          <button type="button" className="btn btn-default">New Article</button> 
        </LinkContainer>
        {contents}
      </div>
    );
  }

  deleteArticle(event, id) { 
    fetch('https://localhost:44360/api/articles/' + id, {  
        method: 'DELETE'
    }).then((response) => response.json())  
        .then((responseJson) => {  
            var deletedId = responseJson.id;

        this.setState({
          articles: this.state.articles.filter(function( obj ) {
            return obj.id !== deletedId;
          })
        });

    })  
  }
}

请注意,我的断点没有在服务器端命中,也没有向服务器发出请求。

App.js中,我设置了ApolloClientApolloProvider如下:

client = new ApolloClient({
    uri: "https://localhost:44360/graphql"
  });

  render() {
    return (
      <ApolloProvider client={this.client}>
        <Layout>
          <Route exact path='/' component={Home} exact={true} />
          <Route path='/articles' component={ArticlesIndex} exact={true} />
          <Route path='/articles/create' component={ArticlesCreate} exact={true} />
          <Route path='/articles/edit/:id' component={ArticlesEdit} exact={true} />
        </Layout>
      </ApolloProvider>
    );
  }

为什么会发生这种情况,我该如何克服它?

编辑:正如我在其他地方看到的那样,我将 graphql npm 包降级为 0.12.3 版本,这导致控制台或其他方面没有错误,但屏幕停留在 Loading... 仍然没有请求被发送到服务器。

编辑 2:我现在已经尝试了列出的每个解决方法 here 并且在尝试了更改 webpack 配置的建议之后,页面不会像以前一样出错,但是仍然没有请求发送到服务器Loading... 永远留在页面上。我的 componentDidMount 函数中的断点被击中,但似乎什么也没发生。

编辑 3:如果我用以下 restful 调用替换 graphql 调用以获取数据,一切都会正常运行,但该项目的目的是探索与 graphql 的反应。 restful 正常运行并且我正在尝试替换的调用如下:

fetch('https://localhost:44360/api/Articles/')
      .then(response => response.json())
      .then(data => {
        this.setState({ articles: data, loading: false });
      });

您在 componentDidMount 中使用了 JSX,但您可以 this.props.client.query({query: GET_ARTICLES}),我对其进行了测试,它可以正常工作。

将您的 articles/index.js 更改为

import React, { Component } from "react";
import { Glyphicon } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap";
import { Query, withApollo } from "react-apollo";
import gql from "graphql-tag";

const GET_ARTICLES = gql`
query GetArticleData {
    articles {
    id
    description
    }
}
`;
class ArticlesIndex extends Component {
state = { articles: [], loading: true };
displayName = ArticlesIndex.name;

componentDidMount = () => {
    const { client } = this.props;
    client
    .query({ query: GET_ARTICLES })
    .then(({ data: { articles } }) => {
        if (articles) {
        this.setState({ loading: false, articles });
        }
    })
    .catch(err => {
        console.log("err", err);
    });
};

renderArticlesTable = articles => {
    return (
    <table className="table">
        <thead>
        <tr>
            <th>Id</th>
            <th>Title</th>
            <th>Description</th>
            <th>Edit</th>
            <th>Delete</th>
        </tr>
        </thead>
        <tbody>
        {articles.map(article => (
            <tr key={article.id}>
            <td>{article.id}</td>
            <td>{article.title}</td>
            <td dangerouslySetInnerHTML={{ __html: article.description }} />
            <td>
                <LinkContainer to={"/articles/edit/" + article.id}>
                <Glyphicon glyph="edit" />
                </LinkContainer>
            </td>
            <td onClick={e => this.deleteArticle(e, article.id)}>
                <Glyphicon glyph="trash" />
            </td>
            </tr>
        ))}
        </tbody>
    </table>
    );
};

render() {
    let contents = this.state.loading ? (
    <p>
        <em>Loading...</em>
    </p>
    ) : (
    this.renderArticlesTable(this.state.articles)
    );

    return (
    <div>
        <h1>Articles</h1>
        <LinkContainer to={"/articles/create"}>
        <button type="button" className="btn btn-default">
            New Article
        </button>
        </LinkContainer>
        {contents}
    </div>
    );
}

deleteArticle(event, id) {
    fetch("https://localhost:44360/api/articles/" + id, {
    method: "DELETE"
    })
    .then(response => response.json())
    .then(responseJson => {
        var deletedId = responseJson.id;

        this.setState({
        articles: this.state.articles.filter(function(obj) {
            return obj.id !== deletedId;
        })
        });
    });
}
}

export default withApollo(ArticlesIndex);

将您的 App.js 更改为

import React, { Component } from "react";
import { Route } from "react-router";
import { Layout } from "./components/Layout";
import { Home } from "./components/Home";
import ArticlesIndex from "./components/articles/Index";
import { ArticlesCreate } from "./components/articles/Create";
import { ArticlesEdit } from "./components/articles/Edit";
import { ApolloProvider } from "react-apollo";
import ApolloClient from "apollo-boost";

export default class App extends Component {
  displayName = App.name;

  client = new ApolloClient({
    uri: "https://localhost:44360/graphql"
  });

  render() {
    return (
      <ApolloProvider client={this.client}>
        <Layout>
          <Route exact path="/" component={Home} exact={true} />
          <Route path="/articles" component={ArticlesIndex} exact={true} />
          <Route
             path="/articles/create"
             component={ArticlesCreate}
             exact={true}
          />
          <Route
            path="/articles/edit/:id"
            component={ArticlesEdit}
            exact={true}
          />
        </Layout>
      </ApolloProvider>
     );
   }
}

问题是您在 ComponentDidMount 中使用了 JSX,这在 React 中不受支持,为了在生命周期方法中使用查询,您应该使用 withApollo 包装组件并且这会将客户端添加到道具中(因此 export default withApollo(ArticlesIndex);),然后您可以在 componentDidMount 中执行 this.props.client.query 然后您可以设置状态。