こんにちは
まず、ご質問に書かれている現状について、指摘させて頂きますと、まず第一に
このようにしたら順にsetStateできると思ったのですが、setStateされるたびにrenderを呼び出す方法がわかりませんでした。
とのことですが、Reactのユーザー自身が、
setStateされるたびにrenderを呼び出す
ためのコードを書く必要はないです。というのは、 setState
によって this.state
に変更があったら、自動的にrender
を呼んでくれる仕組みをReactが提供してくれているからです。
次に、ご質問に挙げられているコードの以下の部分
javascript
1 for(var i=0; i < res.data.array.length; ++ i ){
2 this.setState({
3 name: res.data.array[i].name,
4 storeId: res.data.array[i].id
5 })
6 }
で、array
の要素をループでひとつひとつ取得して、各々に対して setState
していますが、これだとループが終わったときに、this.state
に入っているのは最後の要素の情報だけになってしまいますので、APIのレスポンスが返ってきたら、レスポンスに入っている array
をそのまま this.state.array
として保持させるようにします。したがって、レスポンスが返ってきたら、それに応じた setState
は1回ということになります。
上記をふまえて、以下は具体的なサンプルです。
(ENDPOINT としては、モックAPIサービスのmockable.io に、ご質問にあるJSONを返すように作成しました。)
App.js
jsx
1import React from 'react';
2import axios from 'axios';
3import './style.css';
4
5const ENDPOINT = 'https://demo2746340.mockable.io/q211055/data1.json';
6
7const Item = ({ id, name }) => (
8 <tr>
9 <td>{id}</td>
10 <td>{name}</td>
11 </tr>
12);
13
14class App extends React.Component {
15 constructor(props) {
16 super(props)
17 this.state = { array: [] }
18 }
19
20 componentDidMount() {
21 axios
22 .get(ENDPOINT)
23 .then(res => {
24 this.setState({ array: res.data.array });
25 })
26 }
27
28 render() {
29 return (
30 <table>
31 <tr><th>ID</th><th>NAME</th></tr>
32 {this.state.array.map(e =>
33 <Item key={e.id} id={e.id} name={e.name} />)
34 }
35 </table>
36 );
37 }
38}
39
40export default App;
上記では、JSONの内容を <table>
による表にしています。 <Item>
はarray
に含まれる要素の1個分の内容をrenderするためのFunctionalコンポーネントで、テーブルの1行分になります。
以下は、App
を動作確認するためのいくつかのファイルです。
index.html
html
1<!DOCTYPE html>
2<html lang="ja">
3 <head>
4 <title>sample</title>
5 </head>
6 <body>
7 <div id="root"></div>
8 </body>
9</html>
index.js
javascript
1import React from 'react';
2import ReactDOM from 'react-dom';
3import App from './App';
4
5ReactDOM.render(<App />, document.getElementById('root'));
style.css
css
1table, th, td { border: solid 1px #000; }
2table { border-collapse: collapse; }
3td, th { padding: 10px; }
4th { background-color: #aaa; }
App
がJSONの内容を<table>
として表示するまでの基本的な流れは以下です。
- 初回の
render
では、this.state.array
は空の配列です。これはconstructor
でそのように初期化されているからです。したがって、ヘッダ行のみのテーブルがrender されます。
- 初回の
render
が終わった後、componentDidMount
が呼ばれ、その中でaxiosがENDPOINT からJSONをGETしようとします。
- JSONの取得が成功すると、レスポンスに含まれる array を
this.state.array
として保持するようにsetState
します。配列全体でstate
を更新するので、setState
は一回です。
setState
によってthis.state
が更新されると、再度render
が(冒頭書いたように、自動的に)呼ばれます。
- 再度の
render
のときは this.state.array
は空ではなく、axiosによるGETのレスポンスJSONに含まれるarray の内容に更新されています。これに map を使うことで、 <Item>
の配列を得ます。
- 各
<Item>
は、データ行の1行分にrender されるので、App全体としては以下のように表示されます。
まとめますと、まずは冒頭に書いた
setState
によって this.state
の内容が変更されたら、自動的にrender
を呼んでくれる仕組みをReactが提供してくれている
という点をしっかり押さえておき、その上で、コンポーネントの(componentDidMount
などの)ライフサイクルメソッドの理解を点検するとよいかと思います。
以上、参考になれば幸いです。
追記
このご質問で確認しておく必要のある内容は Reactの基本ですので、公式ドキュメントを拠り所にすることをお勧めします。このご質問の疑問を解消するという目的ですと、 MAIN COCEPTS の章の
あたりです。上記のページで、「クラスにライフサイクルメソッドを追加する 」の節の終わりに、 Clockの完成版について動作の流れが 1. 〜 5. として順に説明されています。その中で
- ・・・ setState() が呼び出されたおかげで、React は state が変わったということが分かるので、render() メソッドを再度呼び出して、画面上に何を表示すべきかを知ります。・・・
と書かれており、上記の原文 は、
4.・・・ Thanks to the setState() call, React knows the state has changed, and calls the render() method again to learn what should be on the screen. ・・・
と書かれています。この中の
Thanks to the setState() call, React knows the state has changed, and calls the render() method again
のことを、私の回答の冒頭では
setState によって this.state に変更があったら、自動的にrenderを呼んでくれる仕組みをReactが提供してくれている
と表現しました。