iT邦幫忙

2022 iThome 鐵人賽

DAY 30
0
Modern Web

Full Stack Web Development 網站實作系列 第 30

Day 30GraphQL(8) - Apollo Client - Gúa ê Tâi-gí chheh tô͘-su-koán(我的台語冊圖書館) app

  • 分享至 

  • xImage
  •  

Apollo client
一個 app 可以透過使用 client object 來和 GraphQL server 溝通,把 App 元件(component) 包裝在 ApolloProvider 元件下, 這樣 client 就可以給 app 下的每一個 component 使用了。

修改 /src/index.js 如下:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

import { ApolloClient, ApolloProvider, HttpLink, InMemoryCache } from '@apollo/client'

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({
    uri: 'http://localhost:4000',
  })
})

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <ApolloProvider client={client}> 
      <App />
  </ApolloProvider>,
);

接下來,使用 React 的 useQuery hook,把資料顯示在螢幕上。
修改 /src/app.js 內容如下:

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

const ALLBOOKS = gql`         // 以下是想要執行的 query 內容
  query {             
    allBooks  {
      title,
      published,
      genres,
      author {
        name,
        born
      }
      id
    }
  }
`;

const DisplayBooks = () => {         // 用 useQuery 執行 ALLBOOKS query
  // useQuery 自動執行我們的 query,並傳回一個 object,
  // 這個 object 包含三個 properties - loading, error 和 data
  const { loading, error, data } = useQuery(ALLBOOKS)

  if (loading) return <p>Loading</p>;
  if (error) return <p>Error :</p>;

  return (
    <div>
      // data 的內容就是 allBooks query 的結果
      {data.allBooks.map(p => p.title).join(', ')}   // 顯示書名在螢幕上
      
    </div>
  )
}

const App =  () => {
  return (
    <div className="App">
      <DisplayBooks />
    </div>
  );
}

export default App;

列出書名如下:
https://ithelp.ithome.com.tw/upload/images/20221015/20129584cXtOyg7doS.png

列出書籍名稱的部分獨立出來,成為一個元件:
新增程式 /src/components/Books.js

const Books = ({ books }) => {
    return (
      <div>
        <h2>books</h2>
  
        <table>
          <tbody>
            <tr>
	      <th></th>
              <th>author</th>
              <th>published</th>
            </tr>
            {books.map((p) => (
              <tr key={p.title}>
                <td>{p.title}</td>
                <td>{p.author.name}</td>
                <td>{p.published}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    )
  }

export default Books

修改 /src/App.js

import Books from './components/Books'

...
const DisplayBooks = () => {
  ...
  return (
    <div>
      <Books books={data.allBooks} />   列出書名 component
    </div>
  );
}

列出書名籍及其他資料:
https://ithelp.ithome.com.tw/upload/images/20221015/20129584tJVRjktfn3.png

查詢單一書籍明細:
需要使用參數(parameters) 去執行 query,參數值是變動的,GraphQL 中的變數(Variables)可以來完成這項任務。
首先,必須為我們的 query 命名,然後給這個 query 一個字串(String)-$titleToSearch,作為參數,這個參數會被給予不同變數。
查詢單一書籍明細的 query 改寫如下:

query FindBookByTitle($titleToSearch: String!) {
    findBook(title: $titleToSearch)  {
      title,
      published,
      genres,
      author {
        name,
        born
      }
      id
    }
  }

在 Apollo server 執行情況:
https://ithelp.ithome.com.tw/upload/images/20221015/20129584bEmqZJt2Lz.png

一樣使用 useQuery,但必須加上 skip 選項。
修改 /src/App.js 如下:

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

import Books from './components/Books'

const ALL_BOOKS  = gql`
  query {
    allBooks {
      title,
      published,
      genres,
      author {
        name,
        born
      }
      id
    }
  }
`;

const App =  () => {
  const result = useQuery(ALL_BOOKS)

  if (result.loading) {
    return <div>loading...</div>
  }
  
  return <Books books={result.data.allBooks} />
}

export default App;

修改 /src/components/Books.js 如下:

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

const FIND_BOOK = gql`
  query FindBookByTitle($titleToSearch: String!) {
    findBook(title: $titleToSearch)  {
      title,
      published,
      genres,
      author {
        name,
        born
      }
      id
    }
  }
`;

const Book = ({ book, onClose }) => {
  return (
    <div>
      <h2>{book.title}</h2>
      <div>
        {book.author.name} {book.author.born}
      </div>
      <div>{book.published}</div>
      <button onClick={onClose}>close</button>
    </div>
  )
}

const Books = ({ books }) => {
  const [titleToSearch, setTitleToSearch] = useState(null)  
  const result = useQuery(FIND_BOOK, {    
    variables: { titleToSearch }, 
    skip: !titleToSearch,  
  })
  
  if (titleToSearch && result.data) { 
    return ( 
      <Book
        book={result.data.findBook}  
        onClose={() => setTitleToSearch(null)} 
      /> 
    ) 
  }

  return (
    <div>
      <h2>Books</h2>
      {books.map((p) => (
        <div key={p.title}>
          {p.title} {p.published} 
          <button onClick={() => setTitleToSearch(p.title)}> 
	          show author
	        </button> 
        </div>
      ))}
    </div>
  )
}
  
export default Books;

執行結果:
https://ithelp.ithome.com.tw/upload/images/20221015/201295844xYFK5mXpL.png
點選 show author 看明細:
https://ithelp.ithome.com.tw/upload/images/20221015/20129584E53L5Dpu6C.png
按 close 回書籍列表。


上一篇
Day 29 GraphQL(7) - Apollo Client - Gúa ê Tâi-gí chheh tô͘-su-koán(我的台語冊圖書館) app
系列文
Full Stack Web Development 網站實作30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言