React-Reduxでアニメーション付きモーダルダイアログを作る

Jan 25, 2017   #react  #redux 

はじめに

ReactとReduxでのモーダルダイアログの表示制御方法と、アニメーションの追加で少しハマったので備忘録。

出来上がったコードはGithubのこのへんこのへん

参考にしたサイト

モーダルダイアログを出す

参考サイトにあげた、Modal Dialogs in Reactをみれば簡単にできるので割愛。

モーダルにアニメーションがつかない

CSSTransitionGroupをrendeしておかないと変更が検知されてもアニメーションが付かないので、参考サイトモーダル表示制御方法のままに実装しようとするとできません。

以下の例だと、renderの先頭でnullを返してしまっている。

class Modal extends React.Component {
  render() {
    if (!this.props.isOpen) {
      return null // XXX: コレがダメ!!!
    }
    const backdropStyle = { ... }
    const modalStyle = { ... }
    return (
      <div>
        <CSSTransitionGroup transitionName="modal"
          transitionEnterTimeout={1000} transitionLeaveTimeout={1000} >
          <div key={this.props.isOpen} style={modalStyle}>
             {this.props.children}
             <div className="footer">
               <button onClick={this.props.toggleModal}>
                 Close
               </button>
             </div>
           </div>
        </CSSTransitionGroup>
      </div>
    )
  }
}

なので初期描画時にもCSSTransitionGroupを返し、その中でモーダルの表示制御を行わなければならない。

class Modal extends React.Component {
  render() {
    const backdropStyle = { ... }
    const modalStyle = { ... }
    return (
      <div style={this.props.isOpen ? backdropStyle : null}>
        <CSSTransitionGroup transitionName="modal"
          transitionEnterTimeout={500}
          transitionLeaveTimeout={500}
        >
        {this.props.isOpen && // CSSTransationGroupの中で表示制御する
          (
            <div style={modalStyle} key={this.props.isOpen}>
              {this.props.children}
              <div className="footer">
                <button onClick={this.props.toggleModal}>
                  Close
                </button>
              </div>
            </div>           
          )
        }
        </CSSTransitionGroup>
      </div>
    )
  }
}

ここまででひとまずアニメーションモーダルができる。。が、まだ問題がある。

モーダルがガタつく

Closeボタンを押した後に一瞬 “ガタッ!” っと見えてしまう。

style={this.props.isOpen ? backdropStyle : null}として、閉じたときにstyleにnullを与えてしまっているので、モーダルが閉じられた瞬間に背景のスタイル当たらなくなり、モーダルの位置がズレる。

場所を固定できるようなスタイルを当ててやらなければならない。そしてこれがひとまずの完成形。

import React from 'react'
import { connect } from 'react-redux'
import { toggleModal } from 'REDUX/actions'
import CSSTransitionGroup from 'react-addons-css-transition-group'

class Modal extends React.Component {
  render() {
    const emptyBackground = {
      position: 'fixed',
      top: 0,
      left: 0,
      width: '100%',
      height: '100%',
      padding: 50  
    }
    const backdropStyle = Object.assign({}, emptyBackground, {
      background: 'rgba(0,0,0,0.5)'
    })  
    const modalStyle = {
      backgroundColor: '#fff',
      borderRadius: 5,
      maxWidth: 500,
      minHeight: 300,
      margin: '0 auto',
      padding: 30
    }
    return (
      <div style={this.props.isOpen ? backdropStyle : null}>
        <CSSTransitionGroup transitionName="modal"
          transitionEnterTimeout={500}
          transitionLeaveTimeout={500}
        >
        {this.props.isOpen && 
          (
            <div style={emptyBackground}>
              <div style={modalStyle} key={this.props.isOpen}>
                {this.props.children}
                <div className="footer">
                  <button onClick={this.props.toggleModal}>
                    Close
                  </button>
                </div>
              </div>           
            </div>
          )
        }
        </CSSTransitionGroup>
      </div>
    )
  }
}
export default connect(
  state => ({isOpen: state.modal.isOpen}),
  {toggleModal}
)(Modal)

おわりに

以上の様に実装すれば、アニメーション付きのモーダルダイアログが作れた。

アニメーションが表示されずにハマってさらにモーダルを閉じるときにもハマって大変だったが、できてしまえば納得だった。

このエントリーをはてなブックマークに追加