前回のReact~基礎編~サイコロアプリケーションでは、ReactのStateを使った簡単なアプリケーション開発を学びました。今回は、ReactのStateパターンを学びましょう。
<目次>
前提条件
React環境構築(Node.js)が構築済みであること
プロジェクト準備
学習に必要なプロジェクトを準備しましょう。
1 2 3 |
create-react-app learning-react-8 cd learning-react-8 npm start |
setStateの仕様
以前、「setState」を使うことで、Stateを更新できることを学びました。
少し復習しておきましょう。
ボタン押下に伴って数値をカウントする簡単なプログラムを作成します。余力がある人は、独力で作ってみてください。

復習は必ずしてください。学習した内容はすぐに忘れます(体験談)
1 |
touch src/DemoSetState.js |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
src/DemoSetState.js import React, { Component } from 'react'; class DemoSetState extends Component { constructor(props) { super(props); this.state = { number: 0 } this.onOneClick = this.onOneClick.bind(this) } onOneClick() { this.setState({number: this.state.number + 1}) } render() { return ( <div> <h1>Number: {this.state.number}</h1> <button onClick={this.onOneClick}>One Click!</button> </div> ); } } export default DemoSetState; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
src/App.js import React from 'react'; import './App.css'; import DemoSetState from './DemoSetState' function App() { return ( <div className="App"> <DemoSetState /> </div> ); } export default App; |


上手く実装できたでしょうか?
Propsと違いStateの値は可変であるため、setStateで書き換え可能なのでしたよね。※Propsは書き換え不可
しかし、注意点があります。
次のプログラムを実行してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
src/DemoSetState.js import React, { Component } from 'react'; class DemoSetState extends Component { constructor(props) { super(props); this.state = { number: 0 } this.onOneClick = this.onOneClick.bind(this); this.onTripleClick = this.onTripleClick.bind(this); } onOneClick() { this.setState({number: this.state.number + 1}); } onTripleClick() { this.setState({number: this.state.number + 1}); this.setState({number: this.state.number + 1}); this.setState({number: this.state.number + 1}); } render() { return ( <div> <h1>Number: {this.state.number}</h1> <button onClick={this.onOneClick}>One Click!</button> <button onClick={this.onTripleClick}>Triple Click!</button> </div> ); } } export default DemoSetState; |
onTripleClick関数を追加しました。一回のクリックで、3回分クリックしたことにできる関数です。
この結果はどうなると思いますか?
プログラムを実行してみましょう。


数値が0->1に変わっていますが、意図した挙動ではありませんね。
setStateは、非同期で処理されます。そのため、setStateを呼んだ時に他の処理が終了しているか否かを考慮しません。
onTripleClickに記述した3つのsetStateは、それぞれStateの状態を書き換えますが、Stateの状態が反映される前に後続の処理が上書きしてしまいます。
例えば、3番目のsetStateの値を変更してみましょう。
1 2 3 |
this.setState({number: this.state.number + 1}); this.setState({number: this.state.number + 1}); this.setState({number: this.state.number + 4}); |
プログラムを実行してみましょう。


numberが0->4に変わりましたね。本当は、6に変化して欲しいのです。
setState Callback Form
「setState Callback Form」を使うと先ほどの事象を解決することができます。
1 2 3 4 |
this.setState(callback) 例) this.setState(curState => ({ count: curState.count + 1 })); |
javaScriptには、コールバック関数というものがあります。関数の中から他の関数を呼び出して実行できるタイプの関数です。
setStateの中でコールバックを行うと、どのようになるか確認してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
DemoSetState.js import React, { Component } from 'react'; class DemoSetState extends Component { constructor(props) { super(props); this.state = { number: 0 } this.onOneClick = this.onOneClick.bind(this); this.onTripleClick = this.onTripleClick.bind(this); } onOneClick() { this.setState({number: this.state.number + 1}); } // onTripleClick() { // this.setState({number: this.state.number + 1}); // this.setState({number: this.state.number + 1}); // this.setState({number: this.state.number + 4}); // } onTripleClick() { this.setState(st => {return {number: st.number + 1}}); this.setState(st => {return {number: st.number + 1}}); this.setState(st => {return {number: st.number + 1}}); } render() { return ( <div> <h1>Number: {this.state.number}</h1> <button onClick={this.onOneClick}>One Click!</button> <button onClick={this.onTripleClick}>Triple Click!</button> </div> ); } } export default DemoSetState; |
onTripleClick関数を書き換えました。動作を確認してみましょう。


意図した挙動になりましたね。
公式サイトでも以下の注意喚起がされています。
setState()
は、コンポーネントを更新するための即時のコマンドではなく、要求として考えてください。パフォーマンスをよくするために、React はそれを遅らせて、単一パスで複数のコンポーネントを更新することがあります。React は state の変更がすぐに適用されることを保証しません。setState()
は常にコンポーネントを直ちに更新するわけではありません。それはバッチ式に更新するか後で更新を延期するかもしれません。これはsetState()
を呼び出した直後にthis.state
を読み取ることが潜在的な危険になります。代わりに、componentDidUpdate
またはsetState
コールバック(setState(updater, callback)
)を使用してください。どちらも更新が適用された後に起動することが保証されています。前の state に基づいて state を設定する必要がある場合は、下記のupdater
引数についてお読みください。
ちなみに次の様な書き方もできます。
1 2 3 4 5 |
function incrementNumber(prevState) { return { count: prevState.count + 1); } this.setState(incrementNumber); |
こちらの方がわかりやすいでしょうか。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
import React, { Component } from 'react'; class DemoSetState extends Component { constructor(props) { super(props); this.state = { number: 0 } this.onOneClick = this.onOneClick.bind(this); this.onTripleClick = this.onTripleClick.bind(this); } onOneClick() { this.setState({number: this.state.number + 1}); } // onTripleClick() { // this.setState({number: this.state.number + 1}); // this.setState({number: this.state.number + 1}); // this.setState({number: this.state.number + 4}); // } // onTripleClick() { // this.setState(st => {return {number: st.number + 1}}); // this.setState(st => {return {number: st.number + 1}}); // this.setState(st => {return {number: st.number + 1}}); // } incrementNumber(curState) { return {number: curState.number + 1}; } onTripleClick() { this.setState(this.incrementNumber); this.setState(this.incrementNumber); this.setState(this.incrementNumber); } render() { return ( <div> <h1>Number: {this.state.number}</h1> <button onClick={this.onOneClick}>One Click!</button> <button onClick={this.onTripleClick}>Triple Click!</button> </div> ); } } export default DemoSetState; |



この辺りは、少し難しいですね。
伝えたいことは、setStateは非同期で処理されるので、2つ以上の処理を行いたいときはコールバック処理を行う必要があるといった内容です。
応用:ロトシックスアプリ

setStateのコールバックを使って、アプリケーション作成に挑戦してみましょう。
プログラミング学習には、アウトプットが必要不可欠です。アプリケーションを作成することで、アプトプットが十全に行えます。
しょぼいのでいいのです。自分の頭を使って、アウトプットに挑戦してみてください。
ここでは、ロトシックスアプリを作ってみます。
<仕様>
・画面上に6つの番号を表示させる
・6つの番号の内、当たった番号の数だけ当選順位が繰り上がる
1 つ当たり -> 参加賞、6つ当たり -> 1等
プロジェクト作成
1 2 3 4 5 6 7 |
create-react-app loto6 cd loto6 touch src/WinningNumber.js touch src/WinningNumber.css touch src/Lottery.js touch src/Lottery.css |
当選番号の作成
最初は、当選番号を作成します。
WinningNumber.jsにその責務を負わせます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
src/WinningNumber.js import React, { Component } from 'react'; import './WinningNumber.css'; class WinningNumber extends Component { render() { return ( <div class="WinningNumber"> {this.props.num} </div> ) } } export default WinningNumber |
続いて、App.jsを編集します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
src/App.js import React from 'react'; import './App.css'; import WinningNumber from './WinningNumber' function App() { return ( <div className="App"> <WinningNumber num={1} /> </div> ); } export default App; |
npm startをして、画面に表示させてみましょう。

次に、番号にスタイリングをつけていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
src/WinningNumber.css .WinningNumber { background-color: rgb(236, 142, 18); border-radius: 50%; color: white; display: inline-block; width: 3em; height: 2.25em; padding-top: 0.75em; text-align: center; margin-right: 0.5em; font-weight: bold; font-size: 1.5em; } |

これを6つ並べて当選番号にします。
当選機能を実装
当選番号の実装が完了したので、次は、当選機能の実装を行いましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
Lottery.js import React, { Component } from 'react'; import WinningNumber from './WinningNumber'; import './Lottery.css'; class Lottery extends Component { static defaultProps = { title: 'Welcome to Loto6', // タイトル winningNumber: 6, // 当選番号の数 maxNum: 10 // 1~10のランダムな数を当選番号として表示 } constructor(props) { super(props); this.state = {nums: Array.from({length: this.props.winningNumber})}; this.handleClick = this.handleClick.bind(this); } slotter() { // コールバック設定 this.setState(curState => ({ nums: curState.nums.map( n => Math.floor(Math.random() * this.props.maxNum) + 1 ) })) } handleClick() { this.slotter(); } render() { return ( <section className='Lottery'> <h1>{this.props.title}</h1> <div> {this.state.nums.map(n => <WinningNumber num={n}/>)} </div> <button onClick={this.handleClick}>Generate</button> </section> ) } } export default Lottery; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
App.js import React from 'react'; import './App.css'; import Lottery from './Lottery'; function App() { return ( <div className="App"> <Lottery /> </div> ); } export default App; |

表示できましたね。
ちなみに、タイトルと当選番号は、Stateにより変更可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
App.js import React from 'react'; import './App.css'; import Lottery from './Lottery'; function App() { return ( <div className="App"> <Lottery /> <Lottery title='mini Loto4' winningNumber={4} maxNum={4} /> </div> ); } export default App; |

スタイルもつけておきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
Lottery.css .Lottery { border: solid 2px tomato; padding: 1em 0 2em 0; text-align: center; margin: 1em auto auto; border-radius: 10px; width: 40em; } .Lottery button { background-color: tomato; color: white; font-size: 120%; border-radius: 10px; margin-top: 1em; padding: 1em; cursor: pointer; box-shadow: 0 4px #a2a2a2; outline: none; } .Lottery button:active { box-shadow: none; position: relative; top: 6px; } |
App.jsも戻しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
App.js import React from 'react'; import './App.css'; import Lottery from './Lottery'; function App() { return ( <div className="App"> <Lottery /> </div> ); } export default App; |

続いて、当選機能を作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
src/Lottery.js import React, { Component } from 'react'; import WinningNumber from './WinningNumber'; import Message from './Message'; import './Lottery.css'; class Lottery extends Component { static defaultProps = { title: 'Welcome to Loto6', winningNumber: 6, maxNum: 2 } constructor(props) { super(props); this.state = { nums: Array.from({length: this.props.winningNumber}), // new lotteryNums: Array.from({length: this.props.winningNumber}), message: "・・・", // new }; this.handleClick = this.handleClick.bind(this); } slotter() { // コールバック設定 this.setState(curState => ({ nums: curState.nums.map( n => Math.floor(Math.random() * this.props.maxNum) + 1 ), // new lotteryNums: curState.nums.map( n => Math.floor(Math.random() * this.props.maxNum) + 1 ) })) } // new checkWinningNumber() { // 当選番号数 var winningNum = 0 for (var i = 0; i < 6; i++){ if (this.state.nums[i] === this.state.lotteryNums[i]) { ++winningNum; } } if (winningNum === 0) this.setState({message: "ハズレ"}); if (winningNum === 1) this.setState({message: "6等当選!"}); if (winningNum === 2) this.setState({message: "5等当選!"}); if (winningNum === 3) this.setState({message: "4等当選!"}); if (winningNum === 4) this.setState({message: "3等当選!"}); if (winningNum === 5) this.setState({message: "2等当選!"}); if (winningNum === 6) this.setState({message: "1等当選!"}) } handleClick() { this.slotter(); this.checkWinningNumber(); } render() { return ( <section className='Lottery'> <h1>{this.props.title}</h1> <div> {this.state.nums.map(n => <WinningNumber num={n}/>)} <Message msg={this.state.message}/> </div> <button onClick={this.handleClick}>Generate</button> </section> ) } } export default Lottery; |
当選番号の配列を作成して、当たりくじと突き合わせする処理を追加しました。
番号が当たった数に応じて、異なるメッセージを生成しています。
メッセージ表示領域には、別のjavaScriptファイルを作成しましょう。
1 2 |
touch Message.js touch Message.css |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
src/Message.js import React, { Component } from 'react'; import './Message.css'; class Message extends Component { render() { return ( <div class="Message"> {this.props.msg} </div> ) } } export default Message |
1 2 3 4 5 6 7 8 9 10 |
src/Message.css .Message { color: tomato; font-size: 3rem; border: 1px solid; width: 400px; margin: 50px auto; padding: } |
動作確認をしてみましょう。
なお、当たりやすいように番号が1及び2のみしか生成されなくしています。



しょぼいですが、こんな感じでガンガンアプリーケーションを作っていきましょう!
次回
もう少しStateに慣れるため、アプリケーションを作成していきましょう。
コメントを残す
コメントを投稿するにはログインしてください。