如何使react.js与zurb一起发挥出色的模态形式

时间:2014-06-16 08:11:17

标签: zurb-foundation reactjs react-jsx zurb-reveal

我正在尝试将zurb揭示与形式整合到反应组件中。到目前为止,下一个代码正确显示模态形式:

ModalForm = React.createClass({
  handleSubmit: function(attrs) {
    this.props.onSubmit(attrs);
    return false;
  },

  render: function(){
    return(
      <div>
        <a href="#" data-reveal-id="formModal" className="button">Add new</a>
        <div id="formModal" className="reveal-modal" data-reveal>
          <h4>Add something new</h4>
          <Form onSubmit={this.handleSubmit} />
          <a className="close-reveal-modal">&#215;</a>
        </div>
      </div>
    );
  }
});

Form组件非常标准:

Form = React.createClass({
  handleSubmit: function() {
    var body = this.refs.body.getDOMNode().value.trim();
    if (!body) {
      return false;
    }
    this.props.onSubmit({body: body});
    this.refs.body.getDOMNode().value = '';
    return false;
  },
  render: function(){
    return(
      <form onSubmit={this.handleSubmit}>
        <textarea name="body" placeholder="Say something..." ref="body" />
        <input type="submit" value="Send" className="button" />
      </form>
    );
  }
}); 

问题:当我在模态表单组件中呈现表单组件并在表单输入中输入内容时,我会在控制台异常Uncaught object中看到。这是一个堆栈:

Uncaught object
  invariant
  ReactMount.findComponentRoot
  ReactMount.findReactNodeByID
  getNode
  ...

如果我只是直接在父组件中渲染表单组件,那么一切正常。请问有人帮忙吗?

2 个答案:

答案 0 :(得分:12)

简而言之,你做错了,这是反应中的错误。

如果您使用任何类型的插件修改反应组件的dom节点,那么它会以某种方式破坏事物。

你应该做的是使用反应本身和互补的css来按照你对模态对话的方式来定位组件。

我建议创建一个使用react statics组件属性的组件来定义包含renderComponent的几个函数,以便为您显示或隐藏反应对话框提供一个很好的干净函数调用。这是我过去使用过的一个减少的例子。注意:它确实使用了jQuery,但如果你不想要jQuery代码,你可以用elementById等标准的js api调用替换jQ。

window.MyDialog = React.createClass({
    propTypes: {
        title:      React.PropTypes.string.isRequired,
        content:    React.PropTypes.string.isRequired
    },
    statics: {

        // open a dialog with props object as props
        open: function(props) {
            var $anchor = $('#dialog-anchor');
            if (!$anchor.length) {
                $anchor = $('<div></div>')
                    .prop('id', 'dialog-anchor');
                    .appendTo('body');
            }
            return React.renderComponent(
                MyDialog(props),
                $anchor.get(0)
            );
        },

        // close a dialog
        close: function() {
            React.unmountComponentAtNode($('#dialog-anchor').get(0));
        }
    },

    // when dialog opens, add a keyup event handler to body
    componentDidMount: function() {
        $('body').on('keyup.myDialog', this.globalKeyupHandler);
    },

    // when dialog closes, clean up the bound keyup event handler on body 
    componentWillUnmount: function() {
        $('body').off('keyup.myDialog');
    },

    // handles keyup events on body
    globalKeyupHandler: function(e) {
        if (e.keyCode == 27) { // ESC key

            // close the dialog
            this.statics.close();
        }
    },

    // Extremely basic dialog dom layout - use your own
    render: function() {
        <div className="dialog">
            <div className="title-bar">
                <div className="title">{this.props.title}</div>
                    <a href="#" className="close" onClick={this.closeHandler}>
                </div>
            </div>
            <div className="content">
                {this.props.content}
            </div>
        </div>
    }
});

然后通过调用:

打开一个对话框

MyDialog.open({title: 'Dialog Title', content: 'My dialog content'});

关闭它

MyDialog.close()

对话框总是附加到身份正下方的新dom节点,其中包含id&#39; dialog-anchor&#39;。如果你打开一个已经打开的对话框,它只会根据新道具更新dom(如果它们相同则不会更新)。

当然,将对话框的内容作为道具参数传递并不是特别有用。我通常会在下面扩展到解析降价 - &gt;内容的html或者在提供url作为prop时,通过组件内部的ajax请求获取一些html。

我知道上面的代码并不完全符合您的要求,但我认为这不是一个很好的方法来让dom修改插件与反应一起工作。你永远不能假设react组件的dom表示是静态的,因此它不能被第三方插件成功操作。老实说,如果你想以这种方式使用反应,你应该重新评估你为什么要使用这个框架。

那就是说,我认为上面的代码是一个对话的一个很好的起点,在这个对话框中,所有操作都发生在组件内部,这毕竟是reactjs的全部内容!

注意:代码是从内存中快速编写的,并没有在其当前形式中进行实际测试,如果有一些轻微的语法错误或其他原因,请对不起。

答案 1 :(得分:5)

以下是迈克所做的事情,但使用zf揭示模态:

var Dialog = React.createClass({
  statics: {
    open: function(){
      this.$dialog = $('#my-dialog');

      if (!this.$dialog.length) {
        this.$dialog = $('<div id="my-dialog" class="reveal-modal" data-reveal role="dialog"></div>')
          .appendTo('body');
      }

      this.$dialog.foundation('reveal', 'open');

      return React.render(
        <Dialog close={this.close.bind(this)}/>,
        this.$dialog[0]
      );
    },
    close: function(){
      if(!this.$dialog || !this.$dialog.length) {
        return;
      }

      React.unmountComponentAtNode(this.$dialog[0]);
      this.$dialog.foundation('reveal', 'close');
    },
  },
  render : function() {
    return (
      <div>
        <h1>This gets rendered into the modal</h1>
        <a href="#" className="button" onClick={this.props.close}>Close</a>
      </div>
    );
  }
});