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;
列出書名如下:
列出書籍名稱的部分獨立出來,成為一個元件:
新增程式 /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>
);
}
列出書名籍及其他資料:
查詢單一書籍明細:
需要使用參數(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 執行情況:
一樣使用 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;
執行結果:
點選 show author 看明細:
按 close 回書籍列表。