ReactJs:使用 Portal "pattern" 包装语义 UI 模态

ReactJs: Wrap Semantic UI Modal using Portal "pattern"

我正在尝试使用描述的门户方法包装语义 UI 模态组件 here

这是我的看法http://jsfiddle.net/mike_123/2wvfjpy9/ 不过,我 运行 遇到了问题,当获得 DOM 参考并将新标记渲染到其中时,似乎仍然保留了旧参考。

render: function() {
    return <div className="ui modal"/>; <-- the idea at first was to only return <div/>
},

...

        React.render(<div > <----------- originally this element had className="ui modal", but this.node doesn't seem to overtake the original node reference
                    <i className="close icon"></i>
                    <div className="header">test</div>
                    <div className="content">
                        {props.children}
                    </div>
                </div>, <----------- 
                this.node);

如何修复此测试用例的任何指示 http://jsfiddle.net/mike_123/2wvfjpy9/

当你调用 this.$modal.modal('show') 时,它实际上会重组你的 DOM,React 不会对此感到高兴。另外,如果您尝试将控件放入您的模式中,该控件将不起作用。

你应该做的是 React.render 一个 已经显示的 模态,即带有标记的模态 $('.ui.modal').modal('show') 已被调用。

这是我尝试使用 "React-Portal" 来帮助在主体级别渲染反应组件。如果您愿意,您仍然可以使用您的方法。

// modal.jsx
import React, { Component } from 'react';
import Portal from 'react-portal';

class InnerModal extends Component {
  constructor(props) {
    super(props);
    this.state = { modalHeight: 0 };
  }

  componentDidMount() {
    let modalHeight = window.$('#reactInnerModal').outerHeight();
    this.setState({modalHeight: modalHeight});
  }

  render() {
    return (
      <div id='reactInnerModal' className='ui standard test modal transition visible active' style={{'margin-top': - this.state.modalHeight / 2}}>
        <i className='close icon' onClick={this.props.closePortal}></i>
        {this.props.children}
      </div>
    );
  }
}

class Modal extends Component {
  render() {
    let triggerButton = <button className='ui button'>Open Modal</button>;
    return (
      <Portal className='ui dimmer modals visible active page transition' openByClickOn={triggerButton} closeOnEsc={true} closeOnOutsideClick={true}>
        <InnerModal>
          {this.props.children}
        </InnerModal>
      </Portal>
    );
  }
}

export default Modal;

请注意,我的模式已在标记中呈现。

然后您可以按如下方式使用模态:

// index.jsx
import React, { Component } from 'react';
import Modal from './modal';

class ModalDemo extends Component {
  render() {
    return (
      <Modal>
        <div className='header'>
          Profile Picture
        </div>
        <div className='image content'>
          <div className='ui medium image'>
            <img src='http://semantic-ui.com/images/avatar2/large/rachel.png' />
          </div>
          <div className='description'>
            <div className="ui header">We've auto-chosen a profile image for you.</div>
            <p>We've grabbed the following image from the <a href='https://www.gravatar.com' target='_blank'>gravatar</a> image associated with your registered e-mail address.</p>
            <p>Is it okay to use this photo?</p>
          </div>
        </div>
        <div className='actions'>
          <div className='ui black deny button'>
            Nope
          </div>
          <div className='ui positive right labeled icon button'>
            Yep, that's me
            <i className='checkmark icon'></i>
          </div>
        </div>
      </Modal>
    );
  }
}

React.render(<ModalDemo />, document.getElementById('content'));

有了这个,您就不必通过 jQuery 和模式中的控件(按钮、link 等)来破解 DOM 操作来调用函数) 仍然有效。

希望对您有所帮助!

Khanetor 彻底而正确地回答了这个问题,我只想提供一个关于如何定位模态框的额外花絮。最好作为评论,但不幸的是,我没有这样做的声誉。

无论如何,Portal 元素的第一个子元素需要绝对定位,以使调光器和生成的模态框位于页面内容的顶部而不是放在页面内容的下方。

首先,将 style={position:'absolute'} 添加到 Modal 的 render 方法中的 Portal 声明中,以便将调光器设置在页面顶部。你最终得到:

<Portal className='ui dimmer modals visible active page transition' openByClickOn={triggerButton} closeOnEsc={true} closeOnOutsideClick={true} style={position:'absolute'}>
        <InnerModal>
          {this.props.children}
        </InnerModal>
</Portal>

接下来,将 InnerModal 的位置设置为 relative 并确定与屏幕顶部的距离。我使用了浏览器视口的八分之一(或 0.125)并得到:

class InnerModal extends Component {
    constructor(props){
        super(props);
        this.state = {
            modalId : _.uniqueId('modal_'),
            style: {}
        }
    }

    componentDidMount() {
        this.setState({
            style : {
                position : 'relative',
                top : $(window).height() * 0.125 + 'px'
            }
        });
    }

    render(){
        return (
            <div id={this.state.modalId} className='ui standard modal transition visible active'
                style={this.state.style}>
                <i className='close icon' onClick={this.props.closePortal}></i>
                { this.props.children }
            </div>
        );
    }
}

通过这些编辑,我终于在 React 中获得了一些可用的模态!希望这对其他人 运行 解决我遇到过的一些相同问题有所帮助。

使用上述方法,您将失去正确的垂直定位和可能的动画。

相反,您可以将模态窗口的组件放在应用程序的根组件中,然后使用 detachable: false 调用 .modal()。使用此选项,语义不会进行任何 DOM 操作,您也不会丢失 React DOM 事件侦听器。

使用 Webpack/Babel 的示例:

import React, { Component } from 'react'
import $ from 'jquery'

if (typeof window !== 'undefined') {
  window.jQuery = $
  require('semantic-ui/dist/semantic.js')
}

class App extends Component {
  state = {
    showModal: false
  }

  _toggleModal = (e) => {
    e.preventDefault()
    this.toggleModalState()
  }

  toggleModalState = () => {
      this.setState({ showModal: !this.state.showModal })  
  }

  render() {
    return (
      <div>
        <a href="" onClick={this._toggleModal}></a>
        {this.state.showModal
          ? <Modal toggleModalState={this.toggleModalState}/>
          : ''}
      </div>
    ) 
  }
}

class Modal extends Component {
  componentDidMount() {   
    $(this.modal)
      .modal({ detachable: false })
      .modal('show')
  }

  componentWillUnmount() {   
    $(this.modal).modal('hide')
  }

  _close = (e) {
    e.preventDefault()
    alert("Clicked")
    this.props.toggleModalState()
  }

  render() {
    return (
      <div ref={(n) => this.modal = n} className="ui modal">
        <div class="content">
          <a onClick={this._close} href="">Click Me</a>
        </div>
      </div>
    )
  }
 }