ReactJs:使用门户“模式”包装语义 UI 模态

IT技术 modal-dialog reactjs portal
2021-05-21 01:12:26

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

这是我的看法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/

3个回答

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

相反,您可以将模态组件放在应用程序的根组件中并.modal()使用detachable: false. 使用此选项,语义不会进行任何 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>
    )
  }
 }

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

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

这是我尝试使用“React-Portal”来帮助渲染身体级别的react组件。如果您愿意,您仍然可以使用您的方法。

// 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 进行 DOM 操作,并且模式中的控件(按钮、链接等,以调用函数)仍然有效。

希望这有帮助!

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

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

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

<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 中获得了一些工作模式!希望这对遇到我遇到的一些相同问题的其他人有所帮助。