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

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

関連記事

wordpressのbogoでサイトで使用する言語を「en-US」から「en」に変更する方法

English 日本語 概要 みなさんこんにちはcandleです。 今回はbogoプラグインのカスタマイズです。bogoはwordpressのサイトを簡単に他言語に対応することができる素晴らしいプラグ …

railsのaws-sdk gemを使ってs3のファイルを削除する

by martinak15 概要 みなさんこんにちはcandleです。今回はs3にアップロードされている画像ファイルをrailsのaws-sdk gemを使用して削除してみたいと思います。 記事中で開 …

fabricを使ってgit のdiffで指定のバージョンからのファイルをアップロード

概要 皆さんこんにちはcandleです。今回はfabricを使ってgit のpush pullを使わないデプロイを実装します。 一般にgit管理されているプロジェクトのデプロイは下の様な図式でデプロイ …

react-modalの背面のスクロールを固定する

概要 みなさんこんにちはcandleです。今回はreact-modalの背面がスクロールした時に動いてしまう問題を解決してみたいと思います。 前提 reactの知識がある 完成版のサンプルコード サン …

ruby on railsのckeditor gemの使い方をscaffoldを用いて解説する

概要 みなさんこんにちはcandleです。今回はruby on railsのckeditorという素晴らしいgemを使って見たいと思います。 ckeditorとはweb版の高機能なwordとかテキスト …


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