著者:山下 賢治
初めまして。香川大学 工学研究科 修士1年の山下賢治です。今回は、JavaScriptライブラリ「React」と、API向けのクエリー言語「GraphQL」を用いて、GitHubで公開されているリポジトリの検索Webアプリケーションを作成します。リポジトリの絞り込みには、開発言語とスター数を利用します。
シェルスクリプトマガジン Vol.68は以下のリンク先でご購入できます。![]()
![]()
図3 「src/auth.js」ファイルに記述する内容
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」ファイルに記述する内容
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」ファイルに記述する内容
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」ファイルに記述する内容
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」ファイルの内容
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;