昨今の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()実行直後)に実行されます。
詳細は以前の記事を参照ください。
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についての詳細は以下を参照ください。
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に実際に実行したファイルも置いておきます。
状態に合わせて表示を切り替る
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
参考ページ
このブログを応援する
お寄せいただいたお気持ちは全額サーバ代や次の記事を執筆するための原資として活用させていただいております。この記事が参考になった場合などぜひご検討ください。
同じカテゴリの記事
- はじめてのReact #30 「CSSフレームワークを導入する」Material-UI テーマをカスタマイズ編
- はじめてのReact #29 「CSSフレームワークを導入する」Material-UI アイコンと文字スタイル編
- はじめてのReact #28 「CSSフレームワークを導入する」Material-UI グリッド編
- はじめてのReact #27 「CSSフレームワークを導入する」Material-UI インストール編
- はじめてのReact #26 「ルーティングに対応する」設定をJSONにまとめる編 react-router@v5
- はじめてのReact #25 「ルーティングに対応する」認証とリダイレクト編 react-router@v5
- はじめてのReact #24 「ルーティングに対応する」URLパラメーター編 react-router@v5
- はじめてのReact #23 「ルーティングに対応する」基本編 react-router@v5
初めまして、React(というかjsやHTMLも…)初心者です。
空の配列として宣言した「monster」にJSONデータをセットしただけで、
「monster.name」や「monster.hp」のように参照できるのが理解できていません。
コンパイル時にエラーになるのかと思っていますが、
取得するJSONデータの構造は別途どこかに定義しておくのでしょうか?
検討違いの質問になっているかも知れませんが、宜しくお願いします。
ブログをご覧いただきありがとうございます。
おそらくTypeScriptを中心に勉強されていらっしゃるのではないかと思うのですが、このページのサンプルはTSではなく通常のJavaScriptです。そのため人間がコンパイルを行う必要はなく、そのままの状態でWebブラウザ上で実行します。またJavaScriptには変数の型は存在しないため、制約なく自由にデータを代入することができます。(データ型自体は存在しますが、変数にはどのようなデータ型でも未指定で代入可能です)
お答えになってますでしょうか?よろしくお願いします。
御返事頂きまして、ありがとうございます。
ご回答の内容も承知しました。
恥ずかしながらチュートリアルやサンプルの流用ばかりで、そもそもTypeScriptを使用している自覚すらありませんでした…。
ただし、個人的にはそういうレベルの質問をしてしまったという事実を理解した事がすごく大きいです。
ありがとうございました。