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

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

関連記事

reactで最もシンプルなドロップダウンメニューを作成

概要 みなさんこんにちはcandleです。今回はreactで最もシンプルなドロップダウンメニューを作ってみたいと思います。 前提 Reactの知識がある 準備 以下のコマンドでreactプロジェクトを …

wordpress skeletonを使った開発環境の構築

はじめに みなさんこんにちはcandleです。wordpressは大変便利なのですが、いまいちgitで管理するのが難しいものです。 というのも、wordpress全体をgit管理対象にしてしまうと、w …

railsのgonで別ページでリロード後turbolinksで移動したら変数がundefindする場合の対処

概要 (追記 2016/05/18 この方法を行うと、turbolinksで問題が起きました。 turbolinksで移動した回数だけ、javascriptが実行されてしまいました。 例えば、 < % ...

railsのaws-sdk gemを使ってs3のフォルダ(folder)を削除する

概要 みなさんこんにちはcandleです。今回はs3にあるフォルダを丸ごとrailsのaws-sdk gemを使用して削除してみたいと思います。 実はこの方法を確立するまですごく四苦八苦しました。なん …

railsのfluent-logger gemを使ってユーザーのアクセスをfluentdサーバに収集する

概要 みなさんこんにちはcandleです。前回からfluentd関連の記事を続けていますが、今回辺りから実用的な使い方を書いていきたいと思います。 fluentdと言えば、ビックデータで扱うようなデー …


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