joppot

コピペで絶対動く。説明を妥協しない

プログラミング

react-modalを利用してモーダルを作成するチュートリアル

投稿日:2018年11月12日 更新日:


概要

みなさんこんにちはcandleです。今回はreact-modalを使ってモーダルを作ってみたいと思います。

react-modalはreactで簡単にモーダルを取り扱えるライブラリです。

しかしながら、実際に使おうとするとreact-modalは簡単ではありません。ステートの管理やアクション、デザインなど細かく作り込んでいく必要があります。

この記事はチュートリアル形式で解説し、一緒にモーダルを作っていきます。

前提

  1. Reactの知識がまあまあある。
  2. 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.jsclass 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はモーダルの中でスクロールし、一番下まで来ると、背面もスクロールできてしまいます。また、モーダルの外側でスクロールしても、同じ様に、背面がスクロールしてしまいます。これは何かと不便なので、固定する方法を次回解説したいと思います。

デザインを整えて、利用してみてください。

スポンサードリンク

「為になったなぁ」と思ったら、シェアお願いします。

-プログラミング
-, , ,

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

wysiwygエディタのSquire editorのサンプルを作成する

概要 みなさんこんにちはcandleです。 今回オープンソースのsquireエディタをhtmlに手動で導入したいと思います。 squireはオープンソースの便利なwysiwygエディタです。 カスタマ …

Rails pluginを使ったgemの作り方[3](vendor/assets 読み込み編)

概要 みなさんこんにちはcandleです。今回はgemでvendorを作成し、それをrailsアプリケーション側で読み込ませる為の方法を紹介します。 よく、gemをインストールすると「app/asse …

React webでsematic-ui-reactを使い基本的なサイトを作成する

概要 みなさんこんにちはcandleです。今回はSemantic UIを使ってReactのデザイン開発をして見たいと思います。 Semantic UIはCSSフレームワークの1つで他に有名なところはT …

wordpressのコメントフォームを編集し、名前とメールのデフォルト値を設定する

概要 みなさんこんにちはcandleです。今回はwordpressのコメントフォームをオーバーライドしてフォームの「名前」「メールアドレス」「url」のデフォルトバリューを設定したいと思います。 コメ …

Twitterbootstrap3の開発環境構築とファイル配置、テンプレートを試す

概要 みなさんこんにちはcandleです。今回はtwitterbootstrap3のダウンロードを行い、ファイルを配置して、開発できる様に準備します。 twitterbootstrap3は最近リリース …


ベンチャー企業のCTOをやってます。大学時代にプログラミングを始め、javaから入門し、C++へて、PHPへと進み、会社ではRailsを使用。自動化が大好きなプログラマー