Skip to content

Instantly share code, notes, and snippets.

@oucb
Created September 13, 2018 01:06
Show Gist options
  • Select an option

  • Save oucb/f6c37db1d543e1090b564e98daaaec12 to your computer and use it in GitHub Desktop.

Select an option

Save oucb/f6c37db1d543e1090b564e98daaaec12 to your computer and use it in GitHub Desktop.

Revisions

  1. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    # Effective GraphQL + Antd
    # 高效的 GraphQL + Antd

    > 本文原载于我的独立博客 https://lutaonan.com/blog/effective-graphql-and-antd/
  2. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,7 @@
    # Effective GraphQL + Antd

    > 本文原载于我的独立博客 https://lutaonan.com/blog/effective-graphql-and-antd/
    在过去的几年,不论是面向内部的系统,还是面向外部的产品,我们都大量地使用了 [Ant.Design](http://ant.design) —— 一个基于 React 的 UI 组件库。

    在做内部系统时,Ant.Design 解决了几乎 60% 的问题。剩下的问题在业务逻辑和代码组织的复杂度。我见过很多内部系统因为滥用状态管理而使代码变得复杂,他们之所以使用状态管理库,并不是因为应用的状态复杂,而是因为需要一个状态树来管理网络请求的状态、接口返回的数据等等这些和接口相关的状态。
  3. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    # Effective GraphQL + Antd

    在过去的几年,不论是面向内部的系统,还是面向外部的产品,我们都大量地使用了 [Ant.Design](http://ant.design) —— 一个基于 React 的 UI 组件库。

    在做内部系统时,Ant.Design 解决了几乎 60% 的问题。剩下的问题在业务逻辑和代码组织的复杂度。我见过很多内部系统因为滥用状态管理而使代码变得复杂,他们之所以使用状态管理库,并不是因为应用的状态复杂,而是因为需要一个状态树来管理网络请求的状态、接口返回的数据等等这些和接口相关的状态。
  4. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -732,8 +732,8 @@ export default class Pagination extends React.Component {

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable%2Fpagination&module=%2Fchapters%2FTable%2Fpagination.tsx)

    ## 总结
    ## What's next

    GraphQL 比 RESTful 的优势在于,GraphQL 让你专注于你想做什么,想获取什么。「查询语言」是声明式的,而「HTTP 请求」是命令式的。声明式可以让复杂度转移给运行时,就像 GraphQL 语句最终执行的 HTTP 请求可以交给像 Apollo 这样的封装去处理。

    当你不再需要自己管理这么多 HTTP 请求的状态时,你就要仔细考虑你的应用到底需不需要状态管理工具了。尤其在开发中后台类的管理系统应用时,往往不会涉及复杂的数据流。[Local state is fine.](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367).
    当你不再需要自己管理这么多 HTTP 请求的状态时,你就要仔细考虑你的应用到底需不需要状态管理工具了。尤其在开发中后台类的管理系统应用时,往往不会涉及复杂的数据流。[Local state is fine](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367).
  5. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 9 additions and 3 deletions.
    12 changes: 9 additions & 3 deletions effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -534,10 +534,10 @@ class CreatePost extends React.Component {
    export default Form.create()(CreatePost);
    ```

    `<Query />` 一样,`<Mutation />` 把请求状态都传递给了 children.

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable%2Fcreate&module=%2Fchapters%2FTable%2Fcreate.tsx)

    `<Query />` 一样,`<Mutation />` 把请求状态都传递给了 children.

    > [官方文档](https://www.apollographql.com/docs/react/essentials/mutations.html) 详细了解 `<Mutation />` 的用法
    ### 操作成功后更新列表数据
    @@ -730,4 +730,10 @@ export default class Pagination extends React.Component {

    ```

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable%2Fpagination&module=%2Fchapters%2FTable%2Fpagination.tsx)
    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable%2Fpagination&module=%2Fchapters%2FTable%2Fpagination.tsx)

    ## 总结

    GraphQL 比 RESTful 的优势在于,GraphQL 让你专注于你想做什么,想获取什么。「查询语言」是声明式的,而「HTTP 请求」是命令式的。声明式可以让复杂度转移给运行时,就像 GraphQL 语句最终执行的 HTTP 请求可以交给像 Apollo 这样的封装去处理。

    当你不再需要自己管理这么多 HTTP 请求的状态时,你就要仔细考虑你的应用到底需不需要状态管理工具了。尤其在开发中后台类的管理系统应用时,往往不会涉及复杂的数据流。[Local state is fine.](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367).
  6. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 13 additions and 13 deletions.
    26 changes: 13 additions & 13 deletions effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -162,7 +162,7 @@ server.listen().then(({ url }) => {
    > 你也可以通过 [Apollo Launchpad](https://launchpad.graphql.com/) 在线上快速搭建一个测试用的 GraphQL 服务.
    ### 前端进行查询
    ### 最简单的前端查询

    有了 GraphQL 服务后,我们开始编写前端组件。首先要创建一个 `ApolloClient` 实例。最简单的方法是通过 `apollo-boost`:

    @@ -193,7 +193,7 @@ const result = await apolloClient.query({
    不过,更高效的做法是用 `<Query />``<Mutation />` 组件进行声明式的查询。因为它们用了 [Function as Child Components
    ](https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9) 的模式,把 `loading` 状态,返回的数据 `data` 都通过参数传递。**你不需要手动去管理请求的状态**

    ```tsx
    ```js
    import { Query, ApolloProvider } from 'react-apollo'
    import gql from 'graphql-tag'
    import { Table } from 'antd'
    @@ -248,14 +248,12 @@ export default () => {

    > `<ApolloProvider />` 的作用是向所有子组件里的 `<Query />``<Mutation />` 传递 `ApolloClient` 实例.
    ##
    ## 进阶实例

    #### 查询参数
    ### 查询参数

    我们希望通过一个下拉框 `<Select />` 选择需要获取的 Post 数量:



    ![undefined](https://ws2.sinaimg.cn/large/0069RVTdgy1fv5m979s27g30lm0g0avf.gif)

    我们可以让 `posts` 查询接受一个 `limit` 参数:
    @@ -298,7 +296,7 @@ const resolvers = {

    在前端,`<Query />``variables` props 可以传递参数:

    ```tsx
    ```js
    import * as React from "react";

    import { Table, Select } from "antd";
    @@ -374,7 +372,7 @@ export default class Limit extends React.Component {

    > [官方文档](https://graphql.org/learn/queries/#variables) 详细了解 GraphQL 查询变量定义
    ### 创建数据
    ### 操作数据 (Mutation)

    接下来实现创建一篇 Post:

    @@ -424,7 +422,7 @@ const resolvers = {

    前端结合 Ant.Design 的 `<Modal />`, `<Form />` 组件和 `react-apollo` 提供的 `<Mutation />` 组件,就可以完成整个「新建 Post」动作:

    ```tsx
    ```js
    const GET_POSTS = gql`
    query GetPost($limit: Int) {
    posts(limit: $limit) {
    @@ -542,7 +540,7 @@ export default Form.create()(CreatePost);

    > [官方文档](https://www.apollographql.com/docs/react/essentials/mutations.html) 详细了解 `<Mutation />` 的用法
    ### 创建成功后更新列表数据
    ### 操作成功后更新列表数据

    成功「新建 Post」以后,通常我们会更新数据列表。`react-apollo` 有两种方法实现。

    @@ -552,7 +550,9 @@ export default Form.create()(CreatePost);

    例如,在获取数据列表的 `<Query />` 中,是通过 `GET_POSTS` 来查询的:

    `query={GET_POSTS} variables={{ limit: 5 }}`
    ```js
    query={GET_POSTS} variables={{ limit: 5 }}
    ```

    那么,在 `update` 回调里,我们可以得到 `GET_POSTS` 对应的 cache, 然后更新这个 cache. 更新 cache 后,通过 `GET_POSTS` (以及相同的 `variables`) 查询的组件,会自动 rerender:

    @@ -612,8 +612,8 @@ const typeDefs = gql`
    + }

    type Query {
    posts(page: Int, limit: Int, sort: String, order: String): [Post]
    + postsWithMeta(page: Int, limit: Int!, sort: String, order: String): PostResultWithMeta!
    posts(page: Int, limit: Int): [Post]
    + postsWithMeta(page: Int, limit: Int!): PostResultWithMeta!
    }
    `
    ```
  7. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -254,7 +254,9 @@ export default () => {

    我们希望通过一个下拉框 `<Select />` 选择需要获取的 Post 数量:

    ![undefined](https://cdn.nlark.com/lark/0/2018/png/6068/1536563666404-83782435-2c7d-4ea1-8fbe-023be40d416d.png)


    ![undefined](https://ws2.sinaimg.cn/large/0069RVTdgy1fv5m979s27g30lm0g0avf.gif)

    我们可以让 `posts` 查询接受一个 `limit` 参数:

  8. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -376,9 +376,7 @@ export default class Limit extends React.Component {

    接下来实现创建一篇 Post:

    ![undefined](https://cdn.nlark.com/lark/0/2018/png/6068/1536577374894-11f9f659-323e-4c12-97cd-6b9051a565c4.png)

    ![undefined](https://cdn.nlark.com/lark/0/2018/png/6068/1536577386676-8767a57f-eb5e-40ff-bebc-04bcc4c603e9.png)
    ![](https://ws2.sinaimg.cn/large/0069RVTdgy1fv5m66us3gg30ls0cm4b0.gif)

    当我们需要操作数据的时候,就要用到 `Mutation`. 还用到一个特殊的数据类型 [Input](https://graphql.org/learn/schema/#input-types). 通常用来在 `Mutation` 的参数里传一整个对象。

    @@ -589,6 +587,8 @@ const refetch = () => {

    Ant.Design 的 `Table` 组件可以通过 `Pagination` 很容易地实现[分页异步加载](https://ant.design/components/table-cn/#components-table-demo-ajax).

    ![](https://ws4.sinaimg.cn/large/0069RVTdgy1fv5m440dg5g30li0e6kjl.gif)

    首先先让 GraphQL 接口支持分页:

    ```diff
  9. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -12,6 +12,8 @@
    > 本文的前端代码在 CodeSandbox https://codesandbox.io/s/pwmrnjz2km
    > 本文使用大量 ES6+ 特性,请在阅读本文前熟悉 ES6+ 语法。
    ## 什么是 GraphQL

    [GraphQL](https://graphql.org) 是一个查询语言,和 SQL 是同等概念的。
    @@ -724,4 +726,6 @@ export default class Pagination extends React.Component {
    }
    }

    ```
    ```

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable%2Fpagination&module=%2Fchapters%2FTable%2Fpagination.tsx)
  10. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 142 additions and 0 deletions.
    142 changes: 142 additions & 0 deletions effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -583,3 +583,145 @@ const refetch = () => {

    这样,所有 query 是 `GET_POSTS` 的组件都会重新执行查询并 rerender.

    ### 分页异步加载

    Ant.Design 的 `Table` 组件可以通过 `Pagination` 很容易地实现[分页异步加载](https://ant.design/components/table-cn/#components-table-demo-ajax).

    首先先让 GraphQL 接口支持分页:

    ```diff
    const typeDefs = gql`
    type Post {
    userId: Int!
    id: Int!
    title: String!
    body: String!
    }

    + type Meta {
    + total: Int!
    + }

    + type PostResultWithMeta {
    + metadata: Meta!
    + data: [Post]!
    + }

    type Query {
    posts(page: Int, limit: Int, sort: String, order: String): [Post]
    + postsWithMeta(page: Int, limit: Int!, sort: String, order: String): PostResultWithMeta!
    }
    `
    ```

    ```diff
    const resolvers = {
    Query: {
    async postsWithMeta(root, args) {
    const {
    page, limit
    } = args
    const res = await http.get('/posts', {
    params: {
    + _page: page,
    _limit: limit
    }
    })
    return {
    + metadata: {
    + total: res.headers['x-total-count']
    + },
    + data: res.data
    }
    }
    },
    }
    ```

    前端就可以传 `limit``page` 实现分页:

    ```js
    const GET_POSTS = gql`
    query GetPosts($limit: Int!, $page: Int) {
    postsWithMeta(limit: $limit, page: $page) {
    metadata {
    total
    },
    data {
    id, title
    }
    }
    }
    `;

    export default class Pagination extends React.Component {

    // 传给 Ant.Design Table 的 pagination 信息
    state = {
    pagination: {
    pageSize: 10,
    current: 1,
    total: 0
    }
    };

    // Query 完成后,给 pagination 设置数据总数
    onCompleteQuery = ({
    postsWithMeta: {
    metadata: { total }
    }
    }) => {
    const pagination = { ...this.state.pagination };
    pagination.total = total;
    this.setState({ pagination });
    };

    handleTableChange = pagination => {
    const pager = { ...pagination };
    pager.current = pagination.current;
    this.setState({ pagination });
    };

    render() {
    return (
    <div style={{ padding: "2rem" }}>
    <Query
    onCompleted={this.onCompleteQuery}
    query={GET_POSTS}
    variables={{
    // 在 pagination 信息中得到 `limit` 和 `page`
    limit: this.state.pagination.pageSize,
    page: this.state.pagination.current
    }}
    >
    {({ loading, data }) => {
    const columns = [
    {
    title: "ID",
    dataIndex: "id"
    },
    { title: "Title", dataIndex: "title" }
    ];

    const dataSource = data.postsWithMeta ? data.postsWithMeta.data : [];

    return (
    <Table
    pagination={this.state.pagination}
    onChange={this.handleTableChange}
    rowKey={record => record.id}
    size="small"
    loading={loading}
    dataSource={dataSource}
    columns={columns}
    />
    );
    }}
    </Query>
    </div>
    );
    }
    }

    ```
  11. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 14 additions and 1 deletion.
    15 changes: 14 additions & 1 deletion effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -569,4 +569,17 @@ const update = (cache, { data: { createPost } }) => {
    }
    ```

    #### 重新执行查询
    #### 重新执行查询

    有时我们想要直接重新请求数据列表而不是手动更新 cache. 我们可以使用 `refetchQueries` 返回一个你要重新查询的查询数组:

    ```js
    const refetch = () => {
    return [
    { query: GET_POSTS }
    ]
    }
    ```

    这样,所有 query 是 `GET_POSTS` 的组件都会重新执行查询并 rerender.

  12. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 27 additions and 0 deletions.
    27 changes: 27 additions & 0 deletions effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -393,6 +393,33 @@ const typeDefs = gql`
    `
    ```

    然后在为 `createPost` 这个 `mutation` 创建一个 `resolver`:

    ```js
    const resolvers = {
    Mutation: {
    async createPost(root, args) {
    const {
    post
    } = args

    const res = await http.post('/posts', {
    data: post
    })

    const now = Date.now()
    const id = Number(now.toString().slice(8, 13))

    return {
    ...res.data.data,
    id,
    userId: 12
    }
    }
    }
    }
    ```

    前端结合 Ant.Design 的 `<Modal />`, `<Form />` 组件和 `react-apollo` 提供的 `<Mutation />` 组件,就可以完成整个「新建 Post」动作:

    ```tsx
  13. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -517,7 +517,7 @@ export default Form.create()(CreatePost);

    成功「新建 Post」以后,通常我们会更新数据列表。`react-apollo` 有两种方法实现。

    1. 更新查询的 Cache
    #### 更新查询的 Cache

    `<Mutation />``update` 这个 props. 在 `mutation` 执行成功后回调,并且带有 `cache``mutation` 的响应数据。我们可以通过更新 `cache` 来实现更新数据列表。

    @@ -542,4 +542,4 @@ const update = (cache, { data: { createPost } }) => {
    }
    ```

    2. 重新执行查询
    #### 重新执行查询
  14. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -370,7 +370,7 @@ export default class Limit extends React.Component {

    > [官方文档](https://graphql.org/learn/queries/#variables) 详细了解 GraphQL 查询变量定义
    ### Create
    ### 创建数据

    接下来实现创建一篇 Post:

  15. @djyde djyde revised this gist Sep 11, 2018. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -513,6 +513,8 @@ export default Form.create()(CreatePost);

    > [官方文档](https://www.apollographql.com/docs/react/essentials/mutations.html) 详细了解 `<Mutation />` 的用法
    ### 创建成功后更新列表数据

    成功「新建 Post」以后,通常我们会更新数据列表。`react-apollo` 有两种方法实现。

    1. 更新查询的 Cache
  16. @djyde djyde renamed this gist Sep 11, 2018. 1 changed file with 24 additions and 4 deletions.
    28 changes: 24 additions & 4 deletions effective-apollo-and-antd.md → effective-graphql-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,3 @@
    # Effective Apollo + Antd
    在过去的几年,不论是面向内部的系统,还是面向外部的产品,我们都大量地使用了 [Ant.Design](http://ant.design) —— 一个基于 React 的 UI 组件库。

    在做内部系统时,Ant.Design 解决了几乎 60% 的问题。剩下的问题在业务逻辑和代码组织的复杂度。我见过很多内部系统因为滥用状态管理而使代码变得复杂,他们之所以使用状态管理库,并不是因为应用的状态复杂,而是因为需要一个状态树来管理网络请求的状态、接口返回的数据等等这些和接口相关的状态。
    @@ -192,7 +191,7 @@ const result = await apolloClient.query({
    不过,更高效的做法是用 `<Query />``<Mutation />` 组件进行声明式的查询。因为它们用了 [Function as Child Components
    ](https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9) 的模式,把 `loading` 状态,返回的数据 `data` 都通过参数传递。**你不需要手动去管理请求的状态**

    ```js
    ```tsx
    import { Query, ApolloProvider } from 'react-apollo'
    import gql from 'graphql-tag'
    import { Table } from 'antd'
    @@ -295,7 +294,7 @@ const resolvers = {

    在前端,`<Query />``variables` props 可以传递参数:

    ```js
    ```tsx
    import * as React from "react";

    import { Table, Select } from "antd";
    @@ -396,7 +395,7 @@ const typeDefs = gql`

    前端结合 Ant.Design 的 `<Modal />`, `<Form />` 组件和 `react-apollo` 提供的 `<Mutation />` 组件,就可以完成整个「新建 Post」动作:

    ```js
    ```tsx
    const GET_POSTS = gql`
    query GetPost($limit: Int) {
    posts(limit: $limit) {
    @@ -518,6 +517,27 @@ export default Form.create()(CreatePost);

    1. 更新查询的 Cache

    `<Mutation />``update` 这个 props. 在 `mutation` 执行成功后回调,并且带有 `cache``mutation` 的响应数据。我们可以通过更新 `cache` 来实现更新数据列表。

    例如,在获取数据列表的 `<Query />` 中,是通过 `GET_POSTS` 来查询的:

    `query={GET_POSTS} variables={{ limit: 5 }}`

    那么,在 `update` 回调里,我们可以得到 `GET_POSTS` 对应的 cache, 然后更新这个 cache. 更新 cache 后,通过 `GET_POSTS` (以及相同的 `variables`) 查询的组件,会自动 rerender:

    ```ts
    const update = (cache, { data: { createPost } }) => {
    // 取得 `GET_POSTS` 对应的 cache
    // 注意要和你要更新的组件的 query 和 variables 都要一致
    const { posts } = cache.readQuery({ query: GET_POSTS, variables: { limit: 5 } })
    // 用 mutation 的响应数据更新 cache
    // 同样,query 和 variables 都要一致
    cache.writeQuery({
    query, GET_POSTS,
    variables: { limit: 5 },
    data: { posts: [createPost].concat(posts) }
    })
    }
    ```

    2. 重新执行查询
  17. @djyde djyde revised this gist Sep 10, 2018. 1 changed file with 174 additions and 16 deletions.
    190 changes: 174 additions & 16 deletions effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -197,16 +197,18 @@ import { Query, ApolloProvider } from 'react-apollo'
    import gql from 'graphql-tag'
    import { Table } from 'antd'

    const GET_POSTS = gql`
    query GetPosts {
    posts {
    id, title
    }
    }
    `

    const App = () => {
    return (
    <Query
    query={gql`
    query {
    posts {
    id, title
    }
    }
    `}
    query={GET_POSTS}
    >
    {({ loading, data }) => {
    const columns = [
    @@ -245,6 +247,8 @@ export default () => {

    > `<ApolloProvider />` 的作用是向所有子组件里的 `<Query />``<Mutation />` 传递 `ApolloClient` 实例.
    ##

    #### 查询参数

    我们希望通过一个下拉框 `<Select />` 选择需要获取的 Post 数量:
    @@ -278,7 +282,7 @@ const resolvers = {
    async posts(root, args) {
    // 每个 resolver 的第二个参数就是查询参数
    const { limit } = args
    const res = await axios.get('https://jsonplaceholder.typicode.com/posts?', {
    const res = await axios.get('https://jsonplaceholder.typicode.com/posts', {
    params: {
    _limit: limit
    }
    @@ -299,6 +303,14 @@ import { Table, Select } from "antd";
    import { Query } from "react-apollo";
    import gql from "graphql-tag";

    const GET_POSTS = gql`
    query GetPosts($limit: Int) {
    posts(limit: $limit) {
    id, title
    }
    }
    `

    export default class Limit extends React.Component {
    state = {
    limit: 5
    @@ -312,13 +324,7 @@ export default class Limit extends React.Component {
    return (
    <div style={{ padding: "2rem" }}>
    <Query
    query={gql`
    query GetPosts($limit: Int) {
    posts(limit: $limit) {
    id, title
    }
    }
    `}
    query={GET_POSTS}
    variables={{ limit: this.state.limit }}
    >
    {({ loading, data }) => {
    @@ -359,7 +365,159 @@ export default class Limit extends React.Component {
    );
    }
    }
    ```

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable%2Flimit&module=%2Fchapters%2FTable%2Flimit.tsx)

    > [官方文档](https://graphql.org/learn/queries/#variables) 详细了解 GraphQL 查询变量定义
    ### Create

    接下来实现创建一篇 Post:

    ![undefined](https://cdn.nlark.com/lark/0/2018/png/6068/1536577374894-11f9f659-323e-4c12-97cd-6b9051a565c4.png)

    ![undefined](https://cdn.nlark.com/lark/0/2018/png/6068/1536577386676-8767a57f-eb5e-40ff-bebc-04bcc4c603e9.png)

    当我们需要操作数据的时候,就要用到 `Mutation`. 还用到一个特殊的数据类型 [Input](https://graphql.org/learn/schema/#input-types). 通常用来在 `Mutation` 的参数里传一整个对象。

    ```js
    const typeDefs = gql`
    input CreatePostInput {
    title: String!
    body: String!
    }
    Mutation {
    createPost(post: CreatePostInput!): Post!
    }
    `
    ```

    前端结合 Ant.Design 的 `<Modal />`, `<Form />` 组件和 `react-apollo` 提供的 `<Mutation />` 组件,就可以完成整个「新建 Post」动作:

    ```js
    const GET_POSTS = gql`
    query GetPost($limit: Int) {
    posts(limit: $limit) {
    id, title
    }
    }
    `;

    // 「新建 Post」 的 Muation
    const CREATE_POST = gql`
    mutation CreatePost($post: CreatePostInput!) {
    createPost(post: $post) {
    id, title
    }
    }
    `

    class CreatePost extends React.Component {
    state = {
    modalVisible: false
    };

    showModal = () => {
    this.setState({ modalVisible: true });
    };

    closeModal = () => {
    this.setState({ modalVisible: false });
    };

    // Modal 的 onOk 事件
    onCreatePost = createPost => {
    const { form } = this.props;
    form.validateFields(async (err, values) => {
    if (!err) {
    // `createPost` 是 `<Mutation />` 组件传给 children 的 mutation 方法
    await createPost({ variables: { post: values } });
    this.closeModal();
    form.resetFields();
    }
    });
    };

    render() {
    const { form } = this.props;

    return (
    <div style={{ padding: "2rem" }}>
    <Query query={GET_POSTS} variables={{ limit: 5 }}>
    {({ loading, data }) => {
    const columns = [
    {
    title: "ID",
    dataIndex: "id"
    },
    { title: "Title", dataIndex: "title" }
    ];

    const dataSource = data.posts || [];
    return (
    <React.Fragment>
    <Mutation mutation={CREATE_POST}>
    {(createPost, { loading, data }) => {
    return (
    <Modal
    onOk={e => this.onCreatePost(createPost)}
    onCancel={this.closeModal}
    title="Create Post"
    confirmLoading={loading}
    visible={this.state.modalVisible}
    >
    <Form>
    <Form.Item label="Title">
    {form.getFieldDecorator("title", {
    rules: [{ required: true }]
    })(<Input />)}
    </Form.Item>
    <Form.Item label="Body">
    {form.getFieldDecorator("body", {
    rules: [{ required: true }]
    })(<Input.TextArea />)}
    </Form.Item>
    </Form>
    </Modal>
    );
    }}
    </Mutation>
    <div style={{ marginBottom: "12px" }}>
    <Button onClick={this.showModal} type="primary">
    New Post
    </Button>
    </div>
    <Table
    rowKey={record => record.id}
    size="small"
    loading={loading}
    dataSource={dataSource}
    columns={columns}
    />
    </React.Fragment>
    );
    }}
    </Query>
    </div>
    );
    }
    }

    export default Form.create()(CreatePost);
    ```

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable%2Flimit&module=%2Fchapters%2FTable%2Flimit.tsx)
    `<Query />` 一样,`<Mutation />` 把请求状态都传递给了 children.

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable%2Fcreate&module=%2Fchapters%2FTable%2Fcreate.tsx)

    > [官方文档](https://www.apollographql.com/docs/react/essentials/mutations.html) 详细了解 `<Mutation />` 的用法
    成功「新建 Post」以后,通常我们会更新数据列表。`react-apollo` 有两种方法实现。

    1. 更新查询的 Cache



    2. 重新执行查询
  18. @djyde djyde revised this gist Sep 10, 2018. 1 changed file with 123 additions and 3 deletions.
    126 changes: 123 additions & 3 deletions effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@

    这篇文章的目标就是让你认识 GraphQL / Apollo, 以及在 Ant.Design 里如何高效地使用他。你不必担心 GraphQL 会给你带来负担,学习和使用 GraphQL 都是令人愉快的过程。你会发现以往让你感到厌烦的需要重复编写的逻辑,可以不必再写了。

    > _Let's keep frontend code lean and straight!_
    > _Keep frontend code lean and straight._
    >
    > —— Randy Lu
    @@ -195,14 +195,15 @@ const result = await apolloClient.query({
    ```js
    import { Query, ApolloProvider } from 'react-apollo'
    import gql from 'graphql-tag'
    import { Table } from 'antd'

    const App = () => {
    return (
    <Query
    query={gql`
    query {
    posts {
    id, title, body
    id, title
    }
    }
    `}
    @@ -240,6 +241,125 @@ export default () => {
    }
    ```

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable)
    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable&module=%2Fchapters%2FTable%2Findex.tsx)

    > `<ApolloProvider />` 的作用是向所有子组件里的 `<Query />``<Mutation />` 传递 `ApolloClient` 实例.
    #### 查询参数

    我们希望通过一个下拉框 `<Select />` 选择需要获取的 Post 数量:

    ![undefined](https://cdn.nlark.com/lark/0/2018/png/6068/1536563666404-83782435-2c7d-4ea1-8fbe-023be40d416d.png)

    我们可以让 `posts` 查询接受一个 `limit` 参数:

    ```diff
    import gql from 'graphql-tag'

    const typeDefs = gql`
    type Post {
    userId: Int!
    id: Int!
    title: String!
    body: String!
    }

    type Query {
    + posts(limit: Int): [Post]
    }
    `
    ```

    然后在 `resolvers` 里拿到参数,进行处理:

    ```js
    const resolvers = {
    Query: {
    async posts(root, args) {
    // 每个 resolver 的第二个参数就是查询参数
    const { limit } = args
    const res = await axios.get('https://jsonplaceholder.typicode.com/posts?', {
    params: {
    _limit: limit
    }
    })
    return res.json()
    }
    }
    }
    ```

    在前端,`<Query />``variables` props 可以传递参数:

    ```js
    import * as React from "react";

    import { Table, Select } from "antd";

    import { Query } from "react-apollo";
    import gql from "graphql-tag";

    export default class Limit extends React.Component {
    state = {
    limit: 5
    };

    onChangeLimit = limit => {
    this.setState({ limit });
    };

    render() {
    return (
    <div style={{ padding: "2rem" }}>
    <Query
    query={gql`
    query GetPosts($limit: Int) {
    posts(limit: $limit) {
    id, title
    }
    }
    `}
    variables={{ limit: this.state.limit }}
    >
    {({ loading, data }) => {
    const columns = [
    {
    title: "ID",
    dataIndex: "id"
    },
    { title: "Title", dataIndex: "title" }
    ];

    const dataSource = data.posts || [];
    return (
    <React.Fragment>
    <div style={{ marginBottom: "12px" }}>
    <Select
    onChange={this.onChangeLimit}
    value={this.state.limit}
    style={{ width: "100px" }}
    >
    <Select.Option value={5}>5</Select.Option>
    <Select.Option value={10}>10</Select.Option>
    <Select.Option value={15}>15</Select.Option>
    </Select>
    </div>
    <Table
    rowKey={record => record.id}
    size="small"
    loading={loading}
    dataSource={dataSource}
    columns={columns}
    />
    </React.Fragment>
    );
    }}
    </Query>
    </div>
    );
    }
    }

    ```

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable%2Flimit&module=%2Fchapters%2FTable%2Flimit.tsx)
  19. @djyde djyde revised this gist Sep 10, 2018. 1 changed file with 80 additions and 56 deletions.
    136 changes: 80 additions & 56 deletions effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,4 @@
    # Effective Apollo + Antd

    在过去的几年,不论是面向内部的系统,还是面向外部的产品,我们都大量地使用了 [Ant.Design](http://ant.design) —— 一个基于 React 的 UI 组件库。

    在做内部系统时,Ant.Design 解决了几乎 60% 的问题。剩下的问题在业务逻辑和代码组织的复杂度。我见过很多内部系统因为滥用状态管理而使代码变得复杂,他们之所以使用状态管理库,并不是因为应用的状态复杂,而是因为需要一个状态树来管理网络请求的状态、接口返回的数据等等这些和接口相关的状态。
    @@ -54,60 +53,17 @@ mutation {

    [Apollo](https://apollographql.com) 是一系列的 GraphQL 工具链,从客户端(不同的前端框架)到服务器端都提供了使用和搭建 GraphQL 的工具。

    ### react-apollo

    `react-apollo` 是 Apollo Client 的 React 封装,它提供 `<Query />``<Mutation />` 组件,可以声明式地做 GraphQL `query``mutation`.

    Query:

    ```tsx
    import gql from 'graphql-tag'
    import { Query } from 'react-apollo'

    const query = gql`
    query {
    posts {
    title, id, body
    }
    }
    `
    下面会通过一个简单的例子,让你从前端到服务器端对 GraphQL 有个初步的了解。

    const App = () => {
    return (
    <Query
    query={query}
    >
    {({ loading, data }) => {

    if (loading) return "Loading..."

    return (
    <div>{data.posts.length}</div>
    )
    }}
    </Query>
    )
    }
    ```

    你可以看到,`<Query />` 是一个高阶组件 ([High Order Component](https://reactjs.org/docs/higher-order-components.html)), 并且用了 [Function as Child Components
    ](https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9) 的模式,把 `loading` 状态,返回的数据 `data` 都通过参数传递。

    ## Real world examples

    对 GraphQL 和 Apollo 有了初步的了解后,下面会通过一些真实的例子,来让你了解到 GraphQL 从服务器端到客户端的开发体验。

    ### 数据展示表格

    现在有一个需求:用表格展示一组数据。
    想象有这样一个需求:用表格展示一组数据。

    ![](https://cdn.nlark.com/lark/0/2018/png/6068/1536227432691-bd70e0e7-aa61-4816-8595-fae1bad6a92b.png)

    后端告诉你,有如下接口:

    - https://jsonplaceholder.typicode.com/posts

    这个接口可以获取所有 `Posts`, 返回的格式如下:
    这个接口可以获取所有 `Post`, 返回的格式如下:

    ```ts
    interface Post {
    @@ -118,7 +74,9 @@ interface Post {
    }
    ```

    #### 搭建一个 GraphQL 服务器
    第一步我们需要搭建一个 GraphQL 服务器。

    ### 搭建一个 GraphQL 服务器

    搭建一个 GraphQL 服务器不难,Apollo Server 对主流的 Node.js Web 框架都有封装,本文不赘述如何搭建一个 GraphQL 服务器,只介绍 GraphQL 后端编写的一些概念。

    @@ -138,12 +96,7 @@ const typeDefs = gql`
    title: String!
    body: String!
    }
    type Query {
    posts: [Post]
    }
    `

    ```

    另外,我们需要对 `Query` 进行定义,来定义有哪些查询操作:
    @@ -204,18 +157,89 @@ server.listen().then(({ url }) => {
    })
    ```

    我在本文用到的 GraphQL 服务器源码在 https://github.com/djyde/graphql-jsonplaceholder , 通过 https://graphql-jsonplaceholder.now.sh 可以访问 Playground.
    > 我在本文用到的 GraphQL 服务器源码在 https://github.com/djyde/graphql-jsonplaceholder , 通过 https://graphql-jsonplaceholder.now.sh 可以访问 Playground.
    > 你也可以通过 [Apollo Launchpad](https://launchpad.graphql.com/) 在线上快速搭建一个测试用的 GraphQL 服务.
    #### 前端进行查询
    ### 前端进行查询

    有了 GraphQL 服务后,我们开始编写前端组件。首先要创建一个 `ApolloClient` 实例。最简单的方法是通过 `apollo-boost`:

    ```ts
    import ApolloClient from "apollo-boost";

    export default new ApolloClient({
    const apolloClient = new ApolloClient({
    // GraphQL 服务器地址
    uri: "https://graphql-jsonplaceholder.now.sh"
    });

    ```

    `ApolloClient` 可以命令式地进行查询:

    ```js
    const result = await apolloClient.query({
    query: gql`
    query {
    posts {
    id, title, body
    }
    }
    `
    })
    ```

    不过,更高效的做法是用 `<Query />``<Mutation />` 组件进行声明式的查询。因为它们用了 [Function as Child Components
    ](https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9) 的模式,把 `loading` 状态,返回的数据 `data` 都通过参数传递。**你不需要手动去管理请求的状态**

    ```js
    import { Query, ApolloProvider } from 'react-apollo'
    import gql from 'graphql-tag'

    const App = () => {
    return (
    <Query
    query={gql`
    query {
    posts {
    id, title, body
    }
    }
    `}
    >
    {({ loading, data }) => {
    const columns = [
    {
    title: "ID",
    dataIndex: "id"
    },
    { title: "Title", dataIndex: "title" }
    ]

    const dataSource = data.posts || []

    return (
    <Table
    size="small"
    loading={loading}
    dataSource={dataSource}
    columns={columns}
    />
    );
    }}
    </Query>
    )
    }

    export default () => {
    return (
    <ApolloProvider client={apolloClient}>
    <App />
    </ApolloProvider>
    )
    }
    ```

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable)

    > `<ApolloProvider />` 的作用是向所有子组件里的 `<Query />``<Mutation />` 传递 `ApolloClient` 实例.
  20. @djyde djyde revised this gist Sep 6, 2018. 1 changed file with 27 additions and 2 deletions.
    29 changes: 27 additions & 2 deletions effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@

    真的需要状态管理库吗?在之前,我没有信心回答这个问题。但在使用了 [GraphQL](https://graphql.org) ([Apollo](https://apollographql.com)) 后,我确信,在大多数场景,你不再需要状态管理。

    这篇文章的目标就是让你认识 GraphQL/Apollo, 以及在 Ant.Design 里如何高效地使用他。你不必担心 GraphQL 会给你带来负担,学习和使用 GraphQL 都是令人愉快的过程。你会发现以往让你感到厌烦的需要重复编写的逻辑,可以不必再写了。
    这篇文章的目标就是让你认识 GraphQL / Apollo, 以及在 Ant.Design 里如何高效地使用他。你不必担心 GraphQL 会给你带来负担,学习和使用 GraphQL 都是令人愉快的过程。你会发现以往让你感到厌烦的需要重复编写的逻辑,可以不必再写了。

    > _Let's keep frontend code lean and straight!_
    >
    @@ -149,7 +149,7 @@ const typeDefs = gql`
    另外,我们需要对 `Query` 进行定义,来定义有哪些查询操作:

    ```diff
    import gql from 'graphql-tag`
    import gql from 'graphql-tag'

    const typeDefs = gql`
    type Post {
    @@ -194,3 +194,28 @@ const resolvers = {

    > [官方文档](https://www.apollographql.com/docs/apollo-server/v2/essentials/data.html) 详细了解 `resolvers` 的用法。
    最后,通过 Apollo Server 把 `typeDefs``resolvers` 连起来,一个 GraphQL 服务器就成功搭起来了。

    ```js
    const server = new ApolloServer({ typeDefs, resolvers })

    server.listen().then(({ url }) => {
    console.log(`Ready at ${url}`)
    })
    ```

    我在本文用到的 GraphQL 服务器源码在 https://github.com/djyde/graphql-jsonplaceholder , 通过 https://graphql-jsonplaceholder.now.sh 可以访问 Playground.

    #### 前端进行查询

    有了 GraphQL 服务后,我们开始编写前端组件。首先要创建一个 `ApolloClient` 实例。最简单的方法是通过 `apollo-boost`:

    ```ts
    import ApolloClient from "apollo-boost";

    export default new ApolloClient({
    // GraphQL 服务器地址
    uri: "https://graphql-jsonplaceholder.now.sh"
    });

    ```
  21. @djyde djyde revised this gist Sep 6, 2018. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -105,7 +105,7 @@ const App = () => {

    后端告诉你,有如下接口:

    https://jsonplaceholder.typicode.com/posts
    - https://jsonplaceholder.typicode.com/posts

    这个接口可以获取所有 `Posts`, 返回的格式如下:

    @@ -129,7 +129,7 @@ interface Post {
    我们已经知道 `Post` 的数据类型是怎样的,就可以编写 `Post` 的类型定义:

    ```js
    import gql from 'graphql-tag`
    import gql from 'graphql-tag'

    const typeDefs = gql`
    type Post {
  22. @djyde djyde revised this gist Sep 6, 2018. 1 changed file with 95 additions and 3 deletions.
    98 changes: 95 additions & 3 deletions effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -18,7 +18,7 @@

    [GraphQL](https://graphql.org) 是一个查询语言,和 SQL 是同等概念的。

    举个例子,在 RESTful 的场景里,我们查询一个资源是命令式地进行网络请求
    举个例子,在 RESTful 的场景里,我们查询一个资源是通过命令式地进行网络请求

    ```js
    const posts = await fetch('/api/v1/posts')
    @@ -37,7 +37,7 @@ query {
    写数据时,命令式地 POST:

    ```js
    const response = await fetch('/api/v1/posts', { method: 'POST', body: {} } )
    const response = await fetch('/api/v1/posts', { method: 'POST', body: { title: "foo", body: "content" } } )
    ```

    使用 GraphQL, 声明式地触发 mutation:
    @@ -91,7 +91,7 @@ const App = () => {
    ```

    你可以看到,`<Query />` 是一个高阶组件 ([High Order Component](https://reactjs.org/docs/higher-order-components.html)), 并且用了 [Function as Child Components
    ](https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9) 的模式,把 `loading` 状态,返回的数据 `data` 都通过函数传递了
    ](https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9) 的模式,把 `loading` 状态,返回的数据 `data` 都通过参数传递

    ## Real world examples

    @@ -102,3 +102,95 @@ const App = () => {
    现在有一个需求:用表格展示一组数据。

    ![](https://cdn.nlark.com/lark/0/2018/png/6068/1536227432691-bd70e0e7-aa61-4816-8595-fae1bad6a92b.png)

    后端告诉你,有如下接口:

    https://jsonplaceholder.typicode.com/posts

    这个接口可以获取所有 `Posts`, 返回的格式如下:

    ```ts
    interface Post {
    userId: number,
    id: number,
    title: string,
    body: string
    }
    ```

    #### 搭建一个 GraphQL 服务器

    搭建一个 GraphQL 服务器不难,Apollo Server 对主流的 Node.js Web 框架都有封装,本文不赘述如何搭建一个 GraphQL 服务器,只介绍 GraphQL 后端编写的一些概念。

    用 Apollo Server 编写 GraphQL 服务器有两个主要概念,`typeDefs``resolvers`.

    `typeDefs` 指的是类型定义。GraphQL 是一个有类型系统的查询语言,因此在编写 GraphQL 服务时,要先对查询的数据类型进行定义。

    我们已经知道 `Post` 的数据类型是怎样的,就可以编写 `Post` 的类型定义:

    ```js
    import gql from 'graphql-tag`
    const typeDefs = gql`
    type Post {
    userId: Int!
    id: Int!
    title: String!
    body: String!
    }
    type Query {
    posts: [Post]
    }
    `
    ```
    另外,我们需要对 `Query` 进行定义,来定义有哪些查询操作:
    ```diff
    import gql from 'graphql-tag`
    const typeDefs = gql`
    type Post {
    userId: Int!
    id: Int!
    title: String!
    body: String!
    }

    + type Query {
    + posts: [Post]
    + }
    `
    ```

    > 在 [官方文档](https://graphql.org/learn/schema/) 详细了解 GraphQL 的类型系统。

    这样一来,外界就可以通过

    ```js
    query {
    posts {
    id, title
    }
    }
    ```

    这样的查询语句查询到 `posts` 了。

    光是类型定义还不够,因为服务器还不知道「查询 posts」这个操作到底应该做什么。这里就是 `resolvers` 要做的事了。在 `resolvers` 里定义查询的实际行为:

    ```js
    const resolvers = {
    Query: {
    async posts() {
    const res = await fetch('https://jsonplaceholder.typicode.com/posts')
    return res.json()
    }
    }
    }
    ```

    > 在 [官方文档](https://www.apollographql.com/docs/apollo-server/v2/essentials/data.html) 详细了解 `resolvers` 的用法。

  23. @djyde djyde revised this gist Sep 6, 2018. 1 changed file with 92 additions and 1 deletion.
    93 changes: 92 additions & 1 deletion effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -9,5 +9,96 @@
    这篇文章的目标就是让你认识 GraphQL/Apollo, 以及在 Ant.Design 里如何高效地使用他。你不必担心 GraphQL 会给你带来负担,学习和使用 GraphQL 都是令人愉快的过程。你会发现以往让你感到厌烦的需要重复编写的逻辑,可以不必再写了。

    > _Let's keep frontend code lean and straight!_
    >
    >
    > —— Randy Lu
    > 本文的前端代码在 CodeSandbox https://codesandbox.io/s/pwmrnjz2km
    ## 什么是 GraphQL

    [GraphQL](https://graphql.org) 是一个查询语言,和 SQL 是同等概念的。

    举个例子,在 RESTful 的场景里,我们查询一个资源是命令式地进行网络请求:

    ```js
    const posts = await fetch('/api/v1/posts')
    ```

    而使用 GraphQL, 是声明式地查询:

    ```gql
    query {
    posts {
    title, body, id
    }
    }
    ```

    写数据时,命令式地 POST:

    ```js
    const response = await fetch('/api/v1/posts', { method: 'POST', body: {} } )
    ```

    使用 GraphQL, 声明式地触发 mutation:

    ```gql
    mutation {
    createPost(post: { title: "foo", body: "content" })
    }
    ```

    你也许会疑惑,这些 GraphQL 语句怎么执行?其实这些语句需要被转换,而转换的工具就是接下来要介绍的 Apollo.

    ## 什么是 Apollo

    [Apollo](https://apollographql.com) 是一系列的 GraphQL 工具链,从客户端(不同的前端框架)到服务器端都提供了使用和搭建 GraphQL 的工具。

    ### react-apollo

    `react-apollo` 是 Apollo Client 的 React 封装,它提供 `<Query />``<Mutation />` 组件,可以声明式地做 GraphQL `query``mutation`.

    Query:

    ```tsx
    import gql from 'graphql-tag'
    import { Query } from 'react-apollo'

    const query = gql`
    query {
    posts {
    title, id, body
    }
    }
    `

    const App = () => {
    return (
    <Query
    query={query}
    >
    {({ loading, data }) => {

    if (loading) return "Loading..."

    return (
    <div>{data.posts.length}</div>
    )
    }}
    </Query>
    )
    }
    ```

    你可以看到,`<Query />` 是一个高阶组件 ([High Order Component](https://reactjs.org/docs/higher-order-components.html)), 并且用了 [Function as Child Components
    ](https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9) 的模式,把 `loading` 状态,返回的数据 `data` 都通过函数传递了。

    ## Real world examples

    对 GraphQL 和 Apollo 有了初步的了解后,下面会通过一些真实的例子,来让你了解到 GraphQL 从服务器端到客户端的开发体验。

    ### 数据展示表格

    现在有一个需求:用表格展示一组数据。

    ![](https://cdn.nlark.com/lark/0/2018/png/6068/1536227432691-bd70e0e7-aa61-4816-8595-fae1bad6a92b.png)
  24. @djyde djyde revised this gist Sep 6, 2018. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,5 @@
    # Effective Apollo + Antd

    在过去的几年,不论是面向内部的系统,还是面向外部的产品,我们都大量地使用了 [Ant.Design](http://ant.design) —— 一个基于 React 的 UI 组件库。

    在做内部系统时,Ant.Design 解决了几乎 60% 的问题。剩下的问题在业务逻辑和代码组织的复杂度。我见过很多内部系统因为滥用状态管理而使代码变得复杂,他们之所以使用状态管理库,并不是因为应用的状态复杂,而是因为需要一个状态树来管理网络请求的状态、接口返回的数据等等这些和接口相关的状态。
    @@ -8,4 +9,5 @@
    这篇文章的目标就是让你认识 GraphQL/Apollo, 以及在 Ant.Design 里如何高效地使用他。你不必担心 GraphQL 会给你带来负担,学习和使用 GraphQL 都是令人愉快的过程。你会发现以往让你感到厌烦的需要重复编写的逻辑,可以不必再写了。

    > _Let's keep frontend code lean and straight!_
    >
    > —— Randy Lu
  25. @djyde djyde revised this gist Sep 6, 2018. 1 changed file with 6 additions and 48 deletions.
    54 changes: 6 additions & 48 deletions effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -1,53 +1,11 @@
    # Effective Apollo + Antd
    在过去的几年,不论是面向内部的系统,还是面向外部的产品,我们都大量地使用了 [Ant.Design](http://ant.design) —— 一个基于 React 的 UI 组件库。

    ## 场景
    在做内部系统时,Ant.Design 解决了几乎 60% 的问题。剩下的问题在业务逻辑和代码组织的复杂度。我见过很多内部系统因为滥用状态管理而使代码变得复杂,他们之所以使用状态管理库,并不是因为应用的状态复杂,而是因为需要一个状态树来管理网络请求的状态、接口返回的数据等等这些和接口相关的状态。

    ### CRUD 表格
    真的需要状态管理库吗?在之前,我没有信心回答这个问题。但在使用了 [GraphQL](https://graphql.org) ([Apollo](https://apollographql.com)) 后,我确信,在大多数场景,你不再需要状态管理。

    ```tsx
    import * as React from "react";
    这篇文章的目标就是让你认识 GraphQL/Apollo, 以及在 Ant.Design 里如何高效地使用他。你不必担心 GraphQL 会给你带来负担,学习和使用 GraphQL 都是令人愉快的过程。你会发现以往让你感到厌烦的需要重复编写的逻辑,可以不必再写了。

    import { Button, Table, Row, Col } from "antd";

    import { Query } from "react-apollo";
    import gql from "graphql-tag";

    export default () => {
    return (
    <Row>
    <Query
    query={gql`
    query {
    posts(limit: 10) {
    id,
    title
    }
    }
    `}
    >
    {({ loading, data }) => {
    const columns = [
    {
    title: "ID",
    dataIndex: "id"
    },
    { title: "Title", dataIndex: "title" }
    ];

    const dataSource = data.posts || [];
    return (
    <Table
    size="small"
    loading={loading}
    dataSource={dataSource}
    columns={columns}
    />
    );
    }}
    </Query>
    </Row>
    );
    };
    ```

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable)
    > _Let's keep frontend code lean and straight!_
    > —— Randy Lu
  26. @djyde djyde revised this gist Sep 6, 2018. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,9 @@
    # Effective Apollo + Antd

    ## 场景

    ### CRUD 表格

    ```tsx
    import * as React from "react";

  27. @djyde djyde revised this gist Sep 6, 2018. 1 changed file with 47 additions and 1 deletion.
    48 changes: 47 additions & 1 deletion effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -1 +1,47 @@
    <iframe src="https://codesandbox.io/embed/pwmrnjz2km?initialpath=%2Ftable" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe>
    ```tsx
    import * as React from "react";

    import { Button, Table, Row, Col } from "antd";

    import { Query } from "react-apollo";
    import gql from "graphql-tag";

    export default () => {
    return (
    <Row>
    <Query
    query={gql`
    query {
    posts(limit: 10) {
    id,
    title
    }
    }
    `}
    >
    {({ loading, data }) => {
    const columns = [
    {
    title: "ID",
    dataIndex: "id"
    },
    { title: "Title", dataIndex: "title" }
    ];

    const dataSource = data.posts || [];
    return (
    <Table
    size="small"
    loading={loading}
    dataSource={dataSource}
    columns={columns}
    />
    );
    }}
    </Query>
    </Row>
    );
    };
    ```

    [![Edit effective-apollo-antd](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pwmrnjz2km?initialpath=%2Ftable)
  28. @djyde djyde created this gist Sep 6, 2018.
    1 change: 1 addition & 0 deletions effective-apollo-and-antd.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    <iframe src="https://codesandbox.io/embed/pwmrnjz2km?initialpath=%2Ftable" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe>