概要
みなさんこんにちはcandleです。今回はreact-modalを使ってモーダルを作ってみたいと思います。
react-modalはreactで簡単にモーダルを取り扱えるライブラリです。
しかしながら、実際に使おうとするとreact-modalは簡単ではありません。ステートの管理やアクション、デザインなど細かく作り込んでいく必要があります。
この記事はチュートリアル形式で解説し、一緒にモーダルを作っていきます。
前提
- Reactの知識がまあまあある。
- create-react-appがインストールされている
開発環境を整える
すでにreactプロジェクトがある方はそちらを利用してください。
ない人は適当なreactプロジェクトを作り、作成できたら移動します。
create-react-app rmodal cd rmodal
Fakerをインストールする
Fakerは解説上必要になるので、絶対必要なものではなりません。
Faker.jsを使ってステートのダミーデータを生成するためにインストールしています。
yarn add --dev faker
App.jsを編集する
fakerを使ったcontainerを作ります。src/App.js
を開いて下の様にします。
import React, { Component } from 'react' import Faker from 'faker' class App extends Component { constructor(props) { super(props) this.state = { users: [], user: { products: [] } } } componentWillMount() { for (let i = 0; i < 10; i++) { let products = [] for (let j = 0; j < 6; j++) { const product = { name: Faker.commerce.productName(), price: Faker.commerce.price(), image: Faker.image.food(30, 40), } products = [...products, product] } const user = { name: Faker.internet.userName(), email: Faker.internet.email(), avatar: Faker.internet.avatar(), products: products, } this.setState(prevState => ({ users: [...prevState.users, user], })) } } renderUsers(user) { return ( <div style={{ border: 'solid 1px #eee' }}> <img src={user.avatar} alt={user.name} width="50" height="50" /> <h4>Name: {user.name}</h4> <h4>Email: {user.email}</h4> </div> ) } render() { return <div>{this.state.users.map(user => this.renderUsers(user))}</div> } } export default App
細かい解説はしませんが、これで、以下の様な、ページが作れます。
今回の目標は、ユーザーをクリックしたら、そのユーザーが持っているプロダクト一覧をモーダルで表示することです。
この部分ですね。
const user = { name: Faker.internet.userName(), email: Faker.internet.email(), avatar: Faker.internet.avatar(), products: products, }
準備ができました。
react-modalをインストールする
ターミナルでコマンドを実行し、react-modalをインストールします。
yarn add react-modal
react-modalのインポート
最初に任意のファイルにreact-modalをインポートします。
私はsrc/App.js
に記述していきます。
import Modal from 'react-modal'
react-modalのsetAppElement
次に、Modal.setAppElement('#yourAppElement')
を書きます。
これはモーダルが表示されたとき、それ以外のコンテンツとの関係をはっきりさせるための設定です。
create-react-appコマンドでプロジェクトを立ち上げた場合、public/index.html
にこの様なDOMがあります。
<body> <noscript> You need to enable JavaScript to run this app. </noscript> <div id="root"></div> <!-- This HTML file is a template. If you open it directly in the browser, you will see an empty page. You can add webfonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the <body> tag. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> </body>
id="root"
が確認できます。これをsrc/App.js
のclass App
前に書いてあげましょう。
Modal.setAppElement('#root')
こんな感じになっています。
モーダルのためのステート
モーダルが開いているのか、閉じているのかを管理するためのステートを作成します。
constructor(props)
のthis.state={}
にmodalIsOpen: false
ステートを追加します。
デフォルトで閉じているため、false
を設定します。
constructor(props) { super(props) this.state = { users: [], user: { products: [] }, modalIsOpen: false } }
モーダルを開いたり閉じたりする関数
次に、Githubにもある様に、モーダルを開いたり、閉じたりする関数を作成します。クラスの好きな場所に、この2つの関数を記述してください。
openModal() { this.setState({modalIsOpen: true}); } closeModal() { this.setState({modalIsOpen: false}); }
モーダルのcss jsonを作成
react-modalのモーダルのデザインはjsonで定義します。
モーダルのcssのデフォルトは以下のコードです。今回は特に変更せずにこのまま使ってみましょう。
記述する場所はどこでも良いのですが、下の方に書いておきましょう。
const customStyles = { overlay: { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(255, 255, 255, 0.75)' }, content: { position: 'absolute', top: '40px', left: '40px', right: '40px', bottom: '40px', border: '1px solid #ccc', background: '#fff', overflow: 'auto', WebkitOverflowScrolling: 'touch', borderRadius: '4px', outline: 'none', padding: '20px' } };
モーダルが開いた時の内容を表示する関数
モーダルの中身はuser
ステートのproducts
を表示します。
なので、この様な関数を作りましょう。
renderProducts(product) { return ( <div style={{ border: 'solid 1px #eee' }}> <img src={product.image} alt={product.name} width="50" height="50" /> <h4>Name: {product.name}</h4> <h4>Price: {product.price}</h4> </div> ) }
モーダルのJSX
大分準備ができたので、モーダルのJSXを作りましょう。render()
関数の中に記述します。場所はどこでも構いませんが、とりあえず、一番下に書きます。
render() { return ( <div> {this.state.users.map(user => this.renderUsers(user))} <Modal isOpen={this.state.modalIsOpen} onRequestClose={this.closeModal.bind(this)} style={customStyles} contentLabel="Example Modal" > <h2> {this.state.user.name} の制作物 </h2> <button onClick={this.closeModal.bind(this)}>Close</button> {this.state.user.products.map(product => this.renderProducts(product))} </Modal> </div> ) }
モーダルを表示する
次に、表示されているユーザーをクリックしたらモーダルが表示されるようにしましょう。openModal()
関数をこの様にます。
openModal(user) { this.setState({ user: user, modalIsOpen: true, }) }
renderUsers(user)
関数を変更します。onClickでopenModal関数を呼び出します。
renderUsers(user) { return ( <div style={{ border: 'solid 1px #eee' }} onClick={this.openModal.bind(this, user)}> <img src={user.avatar} alt={user.name} width="50" height="50" /> <h4>Name: {user.name}</h4> <h4>Email: {user.email}</h4> </div> ) }
これで大枠は完成しました。実際に実行してみましょう。
react-modalの確認
サーバを起動して、
yarn run start
ブラウザで確認します。
モーダルを閉じたいときは、モーダルの外側をクリックするか、Closeボタンをクリックすると閉じることができます。
うまくいきましたね。
全体のコード
最後にここで記述した最終的なコードを書いておきます。
import React, { Component } from 'react' import Faker from 'faker' import Modal from 'react-modal' Modal.setAppElement('#root') class App extends Component { constructor(props) { super(props) this.state = { users: [], user: { products: [], }, modalIsOpen: false, } } componentWillMount() { for (let i = 0; i < 10; i++) { let products = [] for (let j = 0; j < 6; j++) { const product = { name: Faker.commerce.productName(), price: Faker.commerce.price(), image: Faker.image.food(30, 40), } products = [...products, product] } const user = { name: Faker.internet.userName(), email: Faker.internet.email(), avatar: Faker.internet.avatar(), products: products, } this.setState(prevState => ({ users: [...prevState.users, user], })) } } openModal(user) { this.setState({ user: user, modalIsOpen: true, }) } closeModal() { this.setState({ modalIsOpen: false }) } renderProducts(product) { return ( <div style={{ border: 'solid 1px #eee' }}> <img src={product.image} alt={product.name} width="50" height="50" /> <h4>Name: {product.name}</h4> <h4>Price: {product.price}</h4> </div> ) } renderUsers(user) { return ( <div style={{ border: 'solid 1px #eee' }} onClick={this.openModal.bind(this, user)}> <img src={user.avatar} alt={user.name} width="50" height="50" /> <h4>Name: {user.name}</h4> <h4>Email: {user.email}</h4> </div> ) } render() { return ( <div> {this.state.users.map(user => this.renderUsers(user))} <Modal isOpen={this.state.modalIsOpen} onRequestClose={this.closeModal.bind(this)} style={customStyles} contentLabel="Example Modal" > <h2> {this.state.user.name} の制作物 </h2> <button onClick={this.closeModal.bind(this)}>Close</button> {this.state.user.products.map(product => this.renderProducts(product))} </Modal> </div> ) } } const customStyles = { overlay: { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(255, 255, 255, 0.75)', }, content: { position: 'absolute', top: '40px', left: '40px', right: '40px', bottom: '40px', border: '1px solid #ccc', background: '#fff', overflow: 'auto', WebkitOverflowScrolling: 'touch', borderRadius: '4px', outline: 'none', padding: '20px', }, } export default App
まとめ
この記事ではreact-modalの基本的な使い方を解説してきました。
react-modalはモーダルの中でスクロールし、一番下まで来ると、背面もスクロールできてしまいます。また、モーダルの外側でスクロールしても、同じ様に、背面がスクロールしてしまいます。これは何かと不便なので、固定する方法を次回解説したいと思います。
デザインを整えて、利用してみてください。