Componentの状態をthis.setState()
で変化させる度にrender()
が自動的に実行され最新の描画がされるのですが、Reactでは単純なHTMLだけではなくComponent自体を別の物に切り替えることができます。
今回はReactの公式ドキュメント「Conditional Rendering」を単純化したサンプルコードでお送りしします。
ログインComponent
よくあるログインとログアウトの管理用のComponentをイメージしたサンプルになっています。最初はログインしていない状態ですが、ボタンをクリックすることでログイン状態になります。再度ボタンをクリックすると最初の状態(ログアウト)に戻るというシンプルなものです。
サンプル
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>LoginControl</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 LoginControl extends React.Component { constructor(props) { super(props); this.handleLoginClick = this.handleLoginClick.bind(this); this.handleLogoutClick = this.handleLogoutClick.bind(this); this.state = { isLoggedIn: false }; } handleLoginClick() { this.setState({isLoggedIn: true}); } handleLogoutClick() { this.setState({isLoggedIn: false}); } render() { const isLoggedIn = this.state.isLoggedIn; let message; let button; if (isLoggedIn) { message = "ナイス、ログイン"; button = <LogoutButton onClick={this.handleLogoutClick} />; } else { message = "ログインしてください"; button = <LoginButton onClick={this.handleLoginClick} />; } return ( <div> <h1>{message}</h1> {button} </div> ); } } function LogoutButton(props) { return <button onClick={props.onClick}>ログアウト</button>; } function LoginButton(props) { return <button onClick={props.onClick}>ログイン</button>; } ReactDOM.render( <LoginControl />, document.getElementById('root') ); </script> </body> </html>
実行結果
前述の通り最初はログインしていない状態ですが、ボタンをクリックすると
こちらログイン状態になります。もう一度ボタンをクリックすればログアウトし、最初の状態に戻ります。
解説
thisの罠を回避する
コンストラクタに見慣れない部分が出てきました。これは何のために必要なのでしょうか?
constructor(props) { super(props); // 下の2行 this.handleLoginClick = this.handleLoginClick.bind(this); this.handleLogoutClick = this.handleLogoutClick.bind(this);
結論から言うとこれがないとhandleLoginClick()
メソッド内で、Stateの変更を行うことができません。
実際にコメントアウトして実行すると以下のようにCannot read property 'setState' of undefined
と実行時エラーが発生してしまいました。
JavaScriptではthis
というキーワードが頻繁に出てきますが、これは今現在自分がいるオブジェクトを指してくれる便利なキーワードです。しかし今回のように、他のオブジェクトや関数内で実行されるようなケースだとthis
の意味が変わってきてしまいます。LoginControlクラス内のhandleLoginClick()に書かれているthis
は、LoginControlのことを指しています。ところが実際にこのメソッドが実行されるのはLogoutButton関数です。ここでthis
が別の箇所を指すことになってしまいます。
※関数の場合だとそもそもthis
が機能しないのでundefined
となります。
これを防ぎ、本来参照したいthis
を取得するための物だったというわけです。
Functional Component
Componentにはこれまで利用してきたクラス状のものの他にも、関数を使っても作成することができます。 今回のサンプルでいうと以下が関数を使ったComponentです。
function LogoutButton(props) { return <button onClick={props.onClick}>ログアウト</button>; } function LoginButton(props) { return <button onClick={props.onClick}>ログイン</button>; }
関数型とクラス型、どちらが優れている劣っているという物ではなく用途に応じて選択します。ポイントとしては、関数型のComponentにはStateやライフサイクルがありません。逆に言えばJSXを返却するだけのシンプルな処理しかしないのであれば、関数で書いてしまった方がスッキリしますし、高速に動作するはずです。
注意点
必ず上位の階層が必要
render()
内にある以下の箇所ですが、<div>
を消すと構文エラーとなり処理がそこで止まってしまいます。
return ( <div> <h1>{message}</h1> {button} </div> );
Uncaught SyntaxError: Inline Babel script: Unexpected token
という内容のエラーが返されます。
今回のように複数の要素を出力する場合、Reactでは必ず上位の階層があることが求められます。最初のころはこれを知らなくて頭を抱えていましたw
書籍
翔泳社
売り上げランキング: 139,067