iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 9
0
Data Technology

GraphQL + ApolloData 入門系列 第 9

ApolloData & Query 與 Mutation

介紹

前面兩篇ApolloData相關基本設定介紹後,接下來是 graphql 的HOC綁定 gql時候的語法,以及如何使用 Query 與 Mutation

以最下方的github範例 components/PostList.js 這隻的 Query來介紹,先觀察一下 PostList 裡面的 data 有 loading, error, allPosts, _allPostsMeta 這些 props 屬性,另外還有一個 loadMorePosts 的 method ,而這些資料其實是 graphql HOC 綁定 gql 後產生出來的


function PostList ({
  data: { loading, error, allPosts, _allPostsMeta },
  loadMorePosts
})

可以看到graphql 第一個 allPosts 是 gql的 查詢 json payload, 而 options中 第一個 data 就是返回的資料,如果沒有特別命就會返回資料在 props.data, 第二個 props 中寫上一個loadMorePosts method 被綁定的元件 即有這個 props method ,至於 元件綁定後 loading 與 error 是原本 ApolloData 提供的 , allPosts ,_allPostsMeta則是返回資料

export default graphql(allPosts, {
  options: {
    variables: allPostsQueryVars
  },
  props: ({ data }) => ({
    data,
    loadMorePosts: () => {
以下省略    

而loading 當 ApolloData 再發 request 的時候這邊會自動變成 true 所以加載資料可以透過 loading 來切換畫面, 以下是全部 Code 的樣貌 github在下面

import { graphql } from 'react-apollo'
import gql from 'graphql-tag'
import ErrorMessage from './ErrorMessage'
import PostUpvoter from './PostUpvoter'

const POSTS_PER_PAGE = 10

function PostList ({
  data: { loading, error, allPosts, _allPostsMeta },
  loadMorePosts
}) {
  if (error) return <ErrorMessage message='Error loading posts.' />
  if (allPosts && allPosts.length) {
    const areMorePosts = allPosts.length < _allPostsMeta.count
    return (
      <section>
        <ul>
          {allPosts.map((post, index) => (
            <li key={post.id}>
              <div>
                <span>{index + 1}. </span>
                <a href={post.url}>{post.title}</a>
                <PostUpvoter id={post.id} votes={post.votes} />
              </div>
            </li>
          ))}
        </ul>
        {areMorePosts ? (
          <button onClick={() => loadMorePosts()}>
            {' '}
            {loading ? 'Loading...' : 'Show More'}{' '}
          </button>
        ) : (
          ''
        )}
         
      </section>
    )
  }
  return <div>Loading</div>
}

export const allPosts = gql`
  query allPosts($first: Int!, $skip: Int!) {
    allPosts(orderBy: createdAt_DESC, first: $first, skip: $skip) {
      id
      title
      votes
      url
      createdAt
    }
    _allPostsMeta {
      count
    }
  }
`
export const allPostsQueryVars = {
  skip: 0,
  first: POSTS_PER_PAGE
}

// The `graphql` wrapper executes a GraphQL query and makes the results
// available on the `data` prop of the wrapped component (PostList)
export default graphql(allPosts, {
  options: {
    variables: allPostsQueryVars
  },
  props: ({ data }) => ({
    data,
    loadMorePosts: () => {
      return data.fetchMore({
        variables: {
          skip: data.allPosts.length
        },
        updateQuery: (previousResult, { fetchMoreResult }) => {
          if (!fetchMoreResult) {
            return previousResult
          }
          return Object.assign({}, previousResult, {
            // Append the new posts results to the old one
            allPosts: [...previousResult.allPosts, ...fetchMoreResult.allPosts]
          })
        }
      })
    }
  })
})(PostList)

loadMorePosts 的 method 主要是使用 apolloData 提供的 data.fetchMore 這個 method 這邊的 data 就是grpahql 綁定的 gql 語法 ,這邊只是給他不同的 variables skip 抓目前已經有載入多少比數,在使用 updataQuery 把新抓回來的資料在透過新舊疊加,產生出一的一筆 allPosts: [...previousResult.allPosts, ...fetchMoreResult.allPosts] 如果失敗就
返回之前的結果

if (!fetchMoreResult) {
            return previousResult
}
  loadMorePosts: () => {
      return data.fetchMore({
        variables: {
          skip: data.allPosts.length
        },
        updateQuery: (previousResult, { fetchMoreResult }) => {
          if (!fetchMoreResult) {
            return previousResult
          }
          return Object.assign({}, previousResult, {
            // Append the new posts results to the old one
            allPosts: [...previousResult.allPosts, ...fetchMoreResult.allPosts]
          })
        }
      })
    }

接下來看 components/PostUpvoter.js 這隻的 Mutation 的部分
這邊可以看 props 中有兩個 ownProps 與 mutate (ApolloData提供) , ownProps是搭配 optimisticResponse
(樂觀UI,點擊後先反應給使用,之後再執行request) 中看到 updatePost 使用 id: ownProps.id 這個的 ownProps 就是綁定的物件的有重新 render 的效果,mutate 要使用它 ApolloData提供的 mutate 包起來 預設是query不需要

export default graphql(upvotePost, {
  props: ({ ownProps, mutate }) => ({
    upvote: (id, votes) =>
      mutate({
        variables: { id, votes },
        optimisticResponse: {
          __typename: 'Mutation',
          updatePost: {
            __typename: 'Post',
            id: ownProps.id,
            votes: ownProps.votes + 1
          }
        }
      })
  })
})(PostUpvoter)

總結

ApolloData 最常見的Client 使用方式就是 這兩個Query與Mutaion ,而常常搞混的是哪些是 props的對應 也應該要去理解 ApolloData 還有提供哪些 props 可以使用,這個範例中學到的有loading , error, mutate,ownProps

本範例Github連結檔
https://github.com/zeit/next.js/tree/canary/examples/with-apollo


上一篇
ApolloData & ApolloClient 設定檔介紹
下一篇
ApolloData & Query method (一)
系列文
GraphQL + ApolloData 入門30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言