ReactJs: Wrap Semantic UI Modal using a portal "template"

I am trying to link the Semantic UI Modal component using the portal described here

Here is my example: http://jsfiddle.net/mike_123/2wvfjpy9/ I ran into a problem, although when I get the DOM link and render the new markup, it seems to retain the previous link.

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); 

Any pointers on how to fix this test case http://jsfiddle.net/mike_123/2wvfjpy9/

+4
source share
3 answers

You will lose the correct vertical layout and possibly animation with the approaches mentioned above.

Instead, you can simply put your modal component in the root component of your application and call .modal() with .modal() detachable: false . With this option, semantics will not do any DOM manipulation, and you will not lose your React DOM event listeners.

Webpack / Babel example:

 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> ) } } 
+7
source

When you call this.$modal.modal('show') , it will actually rebuild your DOM, and React will not be happy about it. In addition, if you try to establish control in your modal mode, the control will not work.

What you need to do is React.render the modal already shown , i.e. modal with markup, as if $('.ui.modal').modal('show') called.

Here is my attempt to use React-Portal to help render the reactive component at the body level. You can use your method if you want.

 // 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; 

Please note that my modal code has already been displayed in the markup.

Then you can use the modal form as shown below:

 // 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 me <i className='checkmark icon'></i> </div> </div> </Modal> ); } } React.render(<ModalDemo />, document.getElementById('content')); 

With this, you should not crack your way into DOM manipulation using jQuery, and modal control (button, link, etc. for calling functions) still works.

Hope this help!

+5
source

Hanetor answered this question completely and correctly, I just want to make another tidbit on how to position Modal. That would be best as a comment, but unfortunately I do not have such a reputation.

In any case, the first child of the Portal element must be positioned absolutely in order to make the dimmer and the resulting modal sit on top of the content of the page, and not place it below it.

First add style={position:'absolute'} to the Portal declaration in the Modal render method so that a dimmer will be set at the top of the page. You are getting:

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

Then set the InnerModal position to relative and select the distance from the top of the screen. I used the eighth (or 0.125) browser viewport and got:

 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> ); } } 

With these changes, I finally got some working modalities in React! Hope this helps someone who is facing some of the same issues as me.

+2
source

All Articles