ReactでGithubのリポジトリブラウザを作ってみる (1)
GithubのRepositories APIは、指定したユーザーのリポジトリを取得することができます。
$ curl -s 'https://api.github.com/users/toshiyukihina/repos' [ { "id": 38911425, "name": ".emacs.d", "full_name": "toshiyukihina/.emacs.d", "owner": { "login": "toshiyukihina", "id": 1883527, "avatar_url": "https://avatars.githubusercontent.com/u/1883527?v=3", "gravatar_id": "", "url": "https://api.github.com/users/toshiyukihina", "html_url": "https://github.com/toshiyukihina", "followers_url": "https://api.github.com/users/toshiyukihina/followers", "following_url": "https://api.github.com/users/toshiyukihina/following{/other_user}", "gists_url": "https://api.github.com/users/toshiyukihina/gists{/gist_id}", "starred_url": "https://api.github.com/users/toshiyukihina/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/toshiyukihina/subscriptions", ... (snip) ...
このAPIを使って、指定したユーザーのリポジトリ一覧を取得するアプリを作ってみます。
イメージはこんな感じです。
コンポーネントの構成は以下。
- App
- SearchBox
- RepositoryList
- Repository
準備
ここからプロジェクトの雛形をダウンロードして、bundle install --path vendor/bundle && npm install
しておきます。
各コンポーネントのファイルを生成する
$ for c in app search_box repository_list repository; do bundle exec rails g react:component $c; done
Repository
を実装する
Repository
は、以下をパラメータとしてとります。
- name
- description
import React from 'react'; class Repository extends React.Component { render() { return ( <tr> <td>{this.props.name}</td> <td>{this.props.description}</td> </tr> ); } } Repository.PropTypes = { name: React.PropTypes.string.isRequired, description: React.PropTypes.string.isRequired }; export default Repository;
RepositoryList
でRepository
をレンダリングする
Repository
に渡すデータは、RepositoryList
の上位コンポーネントApp
からrepositories
というPropで渡されることにします。
import React from 'react'; import Repository from './repository'; class RepositoryList extends React.Component { render() { const repositoryNodes = this.props.repositories.map((repository) => { return ( <Repository key={repository.id} name={repository.name} description={repository.description} /> ); }); return ( <div> <table> <thead> <tr> <th>Name</th> <th>Description</th> </tr> </thead> <tbody> {repositoryNodes} </tbody> </table> </div> ); } } RepositoryList.PropTypes = { repositories: React.PropTypes.arrayOf(React.PropTypes.object) }; export default RepositoryList;
App
でRepositoryList
をレンダリングする
RepositoryList
に渡すデータは、App
でGithub API経由で取得したデータを渡すことを想定します。
ですので、App
の初期ステートとして固定データを与えておきます。
import React from 'react'; import SearchBox from './search_box'; import RepositoryList from './repository_list'; class App extends React.Component { constructor(props) { super(props); this.state = { repositories: [ { id: '1', name: 'angular-ui-router', description: 'A sample program using angular-ui-router.' }, { id: '2', name: 'angular-chat', description: 'A chat program using Angular.js.' }, { id: '3', name: 'hello-redux', description: 'My first redux.' }, { id: '4', name: 'dotfiles', description: 'My dotfiles.' } ] }; } render() { return ( <div> <SearchBox /> <RepositoryList repositories={this.state.repositories} /> </div> ); } } export default App;
ここまでの実装で、以下のような画面になりました。
SearchBox
の実装
SearchBox
は、入力されたGithubユーザー名を上位コンポーネントであるApp
にコールバックします。
ですので、Propとして渡されたonSubmit
を、Updateボタンクリック時に呼び出します。
import React from 'react'; class SearchBox extends React.Component { constructor(props) { super(props); this.state = { username: '' }; this.handleSubmit = this.handleSubmit.bind(this); this.handleChange = this.handleChange.bind(this); } handleSubmit(e) { e.preventDefault(); this.props.onSubmit(this.state.username); } handleChange(e) { this.setState({username: e.target.value}); } render() { return ( <div> <form onSubmit={this.handleSubmit}> <input type="text" onChange={this.handleChange} placeholder="Enter username" /> <input type="submit" value="Update" /> </form> </div> ); } } SearchBox.PropTypes = { onSubmit: React.PropTypes.func.isRequired }; export default SearchBox;
App
にSearchBox
を組み込む
render()
で、SearchBox
をレンダリングします。
同時に、SearchBox
のUpdateボタンがクリックされた時に呼び出してもらうhandleSubmit()
をPropで渡しています。
HTTPクライアントは、superagentを使っていますので、npm install superagent --save
しています。
あと、初期ステートとしてthis.state.repositories
に与えていたダミーデータは、空の配列にしています。
import React from 'react'; import SearchBox from './search_box'; import RepositoryList from './repository_list'; import request from 'superagent'; class App extends React.Component { constructor(props) { super(props); this.state = { repositories: [] }; this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit(username) { request.get(`https://api.github.com/users/${username}/repos`) .end((err, res) => { if (res.ok) { this.setState({repositories: res.body}); } else { console.error(err); } }); } render() { return ( <div> <SearchBox onSubmit={this.handleSubmit} /> <RepositoryList repositories={this.state.repositories} /> </div> ); } } export default App;
スクリーンキャプチャ
こんな感じです。
ソースコード
ここまでのコードは、release/0.0.1
としてコミットしています。