著者:山下 賢治
初めまして。香川大学 工学研究科 修士1年の山下賢治です。今回は、JavaScriptライブラリ「React」と、API向けのクエリー言語「GraphQL」を用いて、GitHubで公開されているリポジトリの検索Webアプリケーションを作成します。リポジトリの絞り込みには、開発言語とスター数を利用します。
シェルスクリプトマガジン Vol.68は以下のリンク先でご購入できます。
図3 「src/auth.js」ファイルに記述する内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client'; import { setContext } from '@apollo/client/link/context'; const httpLink = createHttpLink({ uri: 'https://api.github.com/graphql', }); const authLink = setContext(() => { const TOKEN = process.env.REACT_APP_TOKEN; return { headers: { Authorization: `Bearer ${TOKEN}`, }, }; }); export const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), }); |
図4 「src/index.js」ファイルに記述する内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import React from 'react'; import ReactDOM from 'react-dom'; import { ApolloProvider } from '@apollo/client'; import { client } from './auth'; import RepoInfo from './components/RepoInfo'; import * as serviceWorker from './serviceWorker'; ReactDOM.render( <React.StrictMode> <ApolloProvider client={client}> <RepoInfo /> </ApolloProvider> </React.StrictMode>, document.getElementById('root') ); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister(); |
図5 「src/graphql/index.js」ファイルに記述する内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import { gql } from '@apollo/client'; export const SEARCH_REPO = gql` query getData($queryString: String!) { search(query: $queryString, type: REPOSITORY, first: 10) { nodes { ... on Repository { databaseId nameWithOwner openGraphImageUrl } } } } `; |
図6 「src/components/RepoInfo.jsx」ファイルに記述する内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
import React, {useState, useEffect, useCallback} from 'react'; import {useLazyQuery} from '@apollo/client'; import { SEARCH_REPO } from '../graphql'; const useRepoData = () => { const [getData, {loading, error, data}] = useLazyQuery(SEARCH_REPO) const [query, setQuery] = useState('') const fetchData = useCallback(() => { getData({ variables: { queryString: query } }); }, [getData, query]); useEffect(() => { fetchData() }, [fetchData]) return [setQuery, {loading, error, data}] } const RepoInfo = () => { const [fetchData, {loading, error,data}] = useRepoData() const handleOnClick = () => { fetchData(`language:python stars:>100`) } if (loading) return <p>Loading Repository</p> if (error) return <p>Error while searching Repository</p> return ( <> <button onClick={handleOnClick}> search </button> { data ? data.search.nodes.map( repo => <div key={repo.databaseId}> <h2>{repo.nameWithOwner}</h2> <img src={repo.openGraphImageUrl} alt='repoImage' /> </div> ) : <></> } </> ) } export default RepoInfo; |
図9 変更後の「src/components/RepoInfo.jsx」ファイルの内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
import React, {useState, useEffect, useCallback} from 'react'; import {useLazyQuery} from '@apollo/client'; import { SEARCH_REPO } from '../graphql'; const useInput = initialValue => { const [value, set] = useState(initialValue) return {value, onChange: (e) => set(e.target.value)} } const useRepoData = () => { const [getData, {loading, error, data}] = useLazyQuery(SEARCH_REPO) const [query, setQuery] = useState('') const fetchData = useCallback(() => { getData({ variables: { queryString: query } }); }, [getData, query]); useEffect(() => { fetchData() }, [fetchData]) return [setQuery, {loading, error, data}] } const RepoInfo = () => { const stars = useInput(0) const language = useInput('') const [fetchData, {loading, error,data}] = useRepoData() const handleOnClick = () => { fetchData(`language:${language.value} stars:>${stars.value}`) } if (loading) return <p>Loading Repository</p> if (error) return <p>Error while searching Repository</p> return ( <> <label> Language : <input type='text' {...language} /> </label> <label> More than : <input type='text' {...stars} /> Stars </label> <button onClick={handleOnClick}> search </button> { data ? data.search.nodes.map( repo => <div key={repo.databaseId}> <h2>{repo.nameWithOwner}</h2> <img src={repo.openGraphImageUrl} alt='repoImage' /> </div> ) : <></> } </> ) } export default RepoInfo; |