Question: Reusable modal in ReactJs

Question

Reusable modal in ReactJs

Answers 1
Added at 2017-01-01 11:01
Tags
Question

I'm trying to create a service which will show a Confirm Dialog and catch user's action (Confirm/Cancel) and can be reusable in any Component with less attempt.

What I tried so far (using React gateway to inject my Dialog)

I create a service like:

var createConfirm = function(options, onConfirm, onCancel) {
  options = options || {};
  var actions = [
    <FlatButton
      label="Cancel"
      primary={true}
      onClick={onCancel}
    />,
    <RaisedButton
      label="OK"
      primary={true}
      onClick={onConfirm}
    />,
  ];
  return (
    <Gateway into="confirm-modal">
      <Dialog
        title={options.title || 'Confirmation'}
        actions={actions}
        modal={true}
        open={true}
      >
        {options.content || 'Are you sure?'}
      </Dialog>
    </Gateway>
  )
}

export default createConfirm;

Then in my component, I inject this to render() function:

...
{
   _this.state.isOpen && _this.confirmable
}

And whenever I need to show Confirm modal, I'll call

this.confirmable = createConfirm({
  title: 'CONFIRM',
  content: 'Are you sure to do that'
}, function() {
  console.log('user confirm')
  _this.setState({
    isOpen: false
  });
}, function() {
  console.log('user cancel')
  _this.setState({
    isOpen: false
  })
});
this.setState({isOpen: true});

My problem

What I do to close that Confirm dialog, as you can see, is set isOpen state to false so it won't be rendered. It's a little cheat and seems not a native way to close a Dialog (lose leaving animation, look weird ...).

So how can I implement a service where I have full control to the Dialog?

Thank you!

Answers
nr: #1 dodano: 2017-01-01 14:01

var Modal = React.createClass({
  displayName: 'Modal',
  backdrop: function() {
    return <div className='modal-backdrop in' />;
  },

  modal: function() {
    var style = {display: 'block'};
    return (
      <div
        className='modal in'
        tabIndex='-1'
        role='dialog'
        aria-hidden='false'
        ref='modal'
        style={style}
      >
        <div className='modal-dialog'>
          <div className='modal-content'>
            {this.props.children}
          </div>
        </div>
      </div>
    );
  },

  render: function() {
    return (
      <div>
        {this.backdrop()}
        {this.modal()}
      </div>
    );
  }
});

var Confirm = React.createClass({
  displayName: 'Confirm',
  getDefaultProps: function() {
    return {
      confirmLabel: 'OK',
      abortLabel: 'Cancel'
    };
  },

  abort: function() {
    return this.promise.reject();
  },

  confirm: function() {
    return this.promise.resolve();
  },

  componentDidMount: function() {
    this.promise = new $.Deferred();
    return React.findDOMNode(this.refs.confirm).focus();
  },

  render: function() {
    var modalBody;
    if (this.props.description) {
      modalBody = (
        <div className='modal-body'>
          {this.props.description}
        </div>
      );
    }

    return (
      <Modal>
        <div className='modal-header'>
          <h4 className='modal-title'>
            {this.props.message}
          </h4>
        </div>
        {modalBody}
        <div className='modal-footer'>
          <div className='text-right'>
            <button
              role='abort'
              type='button'
              className='btn btn-default'
              onClick={this.abort}
            >
              {this.props.abortLabel}
            </button>
            {' '}
            <button
              role='confirm'
              type='button'
              className='btn btn-primary'
              ref='confirm'
              onClick={this.confirm}
            >
              {this.props.confirmLabel}
            </button>
          </div>
        </div>
      </Modal>
    );
  }
});
 
var confirm = function(message, options) {
  var cleanup, component, props, wrapper;
  if (options == null) {
    options = {};
  }
  props = $.extend({
    message: message
  }, options);
  wrapper = document.body.appendChild(document.createElement('div'));
  component = React.render(<Confirm {...props}/>, wrapper);
  cleanup = function() {
    React.unmountComponentAtNode(wrapper);
    return setTimeout(function() {
      return wrapper.remove();
    });
  };
  return component.promise.always(cleanup).promise();
};


$(function() {
  return $('.removable').click(function() {
    return confirm('Are you sure?', {
      description: 'Would you like to remove this item from the list?',
      confirmLabel: 'Yes',
      abortLabel: 'No'
    }).then((function(_this) {
      return function() {
        return $(_this).parent().remove();
      };
    })(this));
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.1/react-with-addons.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.1/JSXTransformer.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">

<div id="row">
    <div class="well">
        <ul>
            <li>Foo <a href="#" class="removable"><i class="glyphicon glyphicon-trash"></i></a></li>
            <li>Bar <a href="#" class="removable"><i class="glyphicon glyphicon-trash"></i></a></li>
            <li>Baz <a href="#" class="removable"><i class="glyphicon glyphicon-trash"></i></a></li>
        </ul>
    </div>
</div>

Source Show
◀ Wstecz