昨今のWebアプリではWebAPI(RESTful API等)をAjaxなどで叩きデータを取得する、もしくはデータを保存するといった行為を日常的に行っているわけですが、今回はAjaxでデータ取得し表示するまでに挑戦してみます。
MonsterView Component
とあるRPGのモンスターの一覧が記録されたJSONファイルをAjaxで取得し、そのままReactで表示するサンプルです。公式ドキュメントの例をちょろっといじったものになります。
サンプル
データファイルです。実際にはサーバ側で動的に出力することが多いと思いますが、今回は静的なファイルで試します。ファイル名はdata.json
。
{ "monsters": [ { "id": 1, "name": "スライム", "hp": 10 }, { "id": 2, "name": "ドラキー", "hp": 12 }, { "id": 3, "name": "おおありくい", "hp": 36 } ] }
実際の処理を行うHTMLです。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>MonsterView</title> <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <!-- Don't use this in production: --> <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script> </head> <body> <div id="root"></div> <script type="text/babel"> class MonsterView extends React.Component { constructor(props) { super(props); this.state ={ isLoaded: false, error: null, monsters: [] } } componentDidMount() { fetch("https://s3-us-west-2.amazonaws.com/blog.katsubemakito.net/static/react1st/18/data.json") .then(res => res.json()) .then( (result) => { this.setState({ isLoaded: true, monsters: result.monsters }); }, (error) => { this.setState({ isLoaded: true, error: error }); } ) } render() { if (this.state.error) { return <div>Error: {this.state.error.message}</div>; } else if ( !this.state.isLoaded ) { return <div>Loading...</div>; } else { return ( <ul> {this.state.monsters.map( monster => ( <li key={monster.id}> {monster.name} {monster.hp} </li> ))} </ul> ); } } } ReactDOM.render( <MonsterView />, document.getElementById('root') ); </script> </body> </html>
実行結果
解説
いつ通信を行ってるの?
以前、Reactが用意している機能にLifeCycleについて取り上げましたが、今回はその中のcomponentDidMount
を利用しています。componentDidMount
メソッド内に処理を書いておくと、ComponentがDOMツリーに追加された直後(render()実行直後)に実行されます。
詳細は以前の記事を参照ください。 blog.katsubemakito.net
Fetch APIでJSON取得
componentDidMount
内でやってることは非常にシンプルです。
詳細は端折りますが、FetchAPIはReactではなく比較的最近のJSで導入された機能になります。以前はXHR(XMLHttpRequest)を利用していたわけですが、その置き換えですね。Promise対応となりCallback地獄から開放された素敵機能です。
通信に成功しても失敗してもthis.setState()
でステートを更新しています。ただし失敗した場合はステートのerror
にエラーメッセージが入ります。
fetch("https://s3-us-west-2.amazonaws.com/blog.katsubemakito.net/static/react1st/18/data.json") .then(res => res.json()) .then( (result) => { this.setState({ isLoaded: true, monsters: result.monsters }); }, (error) => { this.setState({ isLoaded: true, error: error }); } )
FetchAPIについての詳細は以下を参照ください。 blog.katsubemakito.net
jQuery版
ちなみにReactはViewの機能しか提供されませんので、Ajax周りは他のライブラリと組み合わせることも可能です。例えばjQueryで実装する場合は以下のようになります。
headタグなどでjQueryを読み込みます。
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
componentDidMount
をjQueryの$.getJSON
に置き換えてみます。$.ajax
でも大丈夫。
componentDidMount() { let url = "https://s3-us-west-2.amazonaws.com/blog.katsubemakito.net/static/react1st/18/data.json"; $.getJSON(url) .done((json)=>{ this.setState({ isLoaded: true, monsters: json.monsters }); }) .fail(()=>{ this.setState({ isLoaded: true, error: {message:`Can not fetch URL: ${url}`} }); }); }
FetchAPIとあまり変わらないですねw 以下のURLに実際に実行したファイルも置いておきます。 s3-us-west-2.amazonaws.com
状態に合わせて表示を切り替る
render()
はステートの状態にあわせて返却する(表示する)内容を動的に変更しています、エラー時と通信の最中、そして通信に成功したときですね。
render() { if (this.state.error) { return <div>Error: {this.state.error.message}</div>; } else if ( !this.state.isLoaded ) { return <div>Loading...</div>; } else { return ( <ul> {this.state.monsters.map( monster => ( <li key={monster.id}> {monster.name} {monster.hp} </li> ))} </ul> ); } }
書籍
翔泳社
売り上げランキング: 139,067