GraphQL(2) - GraphQL & Apollo client


Posted by TempuraEngineer on 2022-10-31

目錄


Apollo client是甚麼

Apollo client是一個狀態管理用的package,常用於前端為React,後端為GraphQL的專案

透過它可以和本地、遠端的GraphQL API互動

前一篇提到的GraphQL cache問題也以透過它處理


基本使用

  1. 安裝

     npm i @apollo/client
    
     // 用於解析GraphQL的query
     npm i graphql
    
  2. 初始化Apollo client

     // ApolloClient用於建立連線
     // InMemoryCache用於快取住fetch下來的資料
     // ApolloProvider是用於包裹React app的組件
     // gql是一個函式,參數是query string
     import { ApolloClient, InMemoryCache, ApolloProvider, gql } from '@apollo/client';
    
     // 建立連線
     const client = new ApolloClient({
       // API網址,又稱作endpoint
       uri: 'https://flyby-gateway.herokuapp.com/', 
       cache: new InMemoryCache(),
     });
    
  3. query

     client.query({
         query:gql`{
             locations {
                 photo
                 name
             } 
         }`
     }).then(d => console.log(d)); 
    
     // 或者打開dev tool的Network就能看到playload看response
     // response會有撈出來的資料
    
  4. 把client用到React

    使用ApolloProvider組件,可以連接Apollo client和React,其功能類似React的Context.Provider

    使用ApolloProvider後它會取代context,在它底下的組件都可以連到Apollo client,因此官方推薦把它放在index.js

     // index.js
     import React from 'react';
     import ReactDOM from 'react-dom/client';
     import './style/index.css';
     import App from './App'; 
    
     import { ApolloClient, InMemoryCache, ApolloProvider } from     '@apollo/client';
    
     const client = new ApolloClient({
       uri: 'https://flyby-gateway.herokuapp.com/', // API網址
       cache: new InMemoryCache(),
     });
    
     const root = ReactDOM.createRoot(document.getElementById('root'));
    
     root.render(
         <ApolloProvider client={client}>
             <App />
         </ApolloProvider>
     );
    
  5. 使用useQuery撈資料

    useQuery是一個React hook,它會回傳loading、error狀態、data

    也可以設定onCompelete、onError時呼叫哪個方法、做什麼

    Apollo client撈到資料後會自動把它快取住,這樣下次撈同樣的資料時就會更快

     // ApolloTimeline.js
    
     import { useQuery, gql } from '@apollo/client';
    
     export default function ApolloTimeline(props){
       const { loading, error, data } = useQuery(gql`
         {
           histories(sort: "event_date_utc") {
             event_date_utc
             title
             details
             flight {
               launch_success
             }          
           }
         }
       `);
    
       if (loading) return <p>loading...</p>;
       if (error) return <p>Error :(</p>;
    
       return (
         <div>
           <Timeline position="alternate">
             {
               data.histories.map((i, index) => (
                 <TimelineItem key={index}>
                   <TimelineOppositeContent>    {timeFormatter(i.event_date_utc)}</TimelineOppositeContent>
                   <TimelineSeparator>
                     <TimelineDot color="secondary" />
                     {index === data.histories.length - 1? null : <TimelineConnector />}
                   </TimelineSeparator>
                   <TimelineContent>
                     <Chip label={i.title} color={i.flight?.launch_success? 'success' : 'default'}></Chip>
                   </TimelineContent>
                 </TimelineItem>
               ))
             }
           </Timeline>
         </div>
       )
     }
    


refetch & variable

refetch常用於根據使用者的動作撈資料的情況,例如切換分頁

// ApolloRefetch.js

import { useQuery, gql } from '@apollo/client';

import Chip from '@mui/material/Chip';
import Paper from '@mui/material/Paper';
import CircularProgress from '@mui/material/CircularProgress';

function timeFormatter(str){
  if(!str) return '';
  return str.replace(/T|Z/g, ' ');
}

export default function ApolloRefetch(props){
  const queryMultiple = () => {
    // eslint-disable-next-line react-hooks/rules-of-hooks
    const res1 = useQuery(gql`
    query historyId{
      histories{
        id
      }
    }
  `);
    // eslint-disable-next-line react-hooks/rules-of-hooks
    const res2 = useQuery(gql`
    query history($id:ID!){
      history(id:$id){
        details
        event_date_utc
        flight {
          launch_success
          mission_name
        }
        title
        id
      }
    }
    `, {variables:{id:'1'}});
    return [res1, res2];
  };

  const [
      { loading: isIdLoading, error: isIdError, data: idData },
      { loading, data, refetch }
  ] = queryMultiple();


  if (isIdLoading) return <p>loading...</p>;
  if (isIdError) return <p>Error :(</p>;

  return (
    <div className='h-screen p-2'>
        <div className='flex justify-around'>
          {
              idData?.histories?.map((i, index) => <Chip key={index} 
                                                        color={data.history?.id === i.id? 'info' : 'default'} 
                                                        label={i.id} 
                                                        onClick={() => refetch({id:i.id})}></Chip>)
          }
        </div>


        <Paper elevation={3} sx={{padding:4, margin:4, display:'flex', flexDirection:'column', justifyContent:'center', alignItems:'center'}}>
          {
            loading? <CircularProgress/> : <>
                                            <h1 className='text-xl bold border-b-4 border-double border-zinc-600 mb-4 pb-1'>{data.history?.title}</h1>
                                            <p className='mb-2'>{timeFormatter(data.history?.event_date_utc)}</p>
                                            <p className='mb-2'>{data.history?.details}</p>
                                            <p className='mb-2'>{data.history?.flight?.mission_name}</p>
                                            <p>{data.history?.flight?.launch_success}</p>
                                          </>
          }
        </Paper>
    </div>
  )
}


參考資料

Get started with Apollo Client
Apollo client - Queries
Refetching queries in Apollo Client
Cleaning Unwanted Fields From GraphQL Responses

playground

FlyBy GraphQL API


#graphql #Apollo client







Related Posts

變調的 Hacktoberfest 與 amazing project

變調的 Hacktoberfest 與 amazing project

Vite系列#安裝vite&在 Composition API 及 Options API 進行切換

Vite系列#安裝vite&在 Composition API 及 Options API 進行切換

Day00 - CERN ROOT 教學 | 動機、這是什麼

Day00 - CERN ROOT 教學 | 動機、這是什麼


Comments