【问题标题】:React refs with components用组件反应 refs
【发布时间】:2014-11-14 12:01:31
【问题描述】:

我有一个 React 表单,它有一个用于呈现下拉菜单的组件,因为选项来自 API。但是,我无法访问嵌入式组件的 ref。我正在整理我的第一个表单,并试图了解解决此问题的最佳方法。

var ActivityForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();

    var noteCategoryId = this.refs.note_category_id.getDOMNode().value.trim();
    var content = this.refs.content.getDOMNode().value.trim();

    if (!category || !content) {
      return;
    }

    // this.props.onCommentSubmit({author: author, text: text});

    this.refs.note_category_id.getDOMNode().value = '';
    this.refs.content.getDOMNode().value = '';
    return;
  },
  render: function() {
    return (
      <div className="new-activity">
        <h3>New Activity</h3>
        <form onSubmit={this.handleSubmit}>
          <textarea ref='content' />
          <br />

          <label>Category</label>
          <ActivityFormCategoryDropdown /> # THE REF IN THIS COMPONENT ISN'T ACCESSIBLE
          <br />

          <input type="submit" value="Add Activity" />
        </form>
      </div>
    );
  }
});

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    复合组件可以有自己的引用;您可以访问它们以访问组件层次结构的更下方的引用。

    例子:

    <script src="http://fb.me/react-with-addons-0.11.2.js"></script>
    <script src="http://fb.me/JSXTransformer-0.11.2.js"></script>
    
    <div id="app"></div>
    
    <script type="text/jsx">
    /** @jsx React.DOM */
    
    var Parent = React.createClass({
      render: function() {
        return (
          <div>
            <Child ref="child" />
            <div><button onClick={this.handleClick}>Alert Text</button></div>
          </div>
        );
      },
      
      handleClick: function() {
        alert(this.refs.child.refs.textarea.getDOMNode().value);
      }
    });
    
    var Child = React.createClass({
      render: function() {
        return <textarea ref="textarea" />;
      }
    });
    
    React.renderComponent(<Parent />, document.getElementById("app"));
    </script>

    但是,ssorallen 是正确的——如果可能,您应该尽量避免这种情况。相反,您应该将回调传递给子级:

    <script src="http://fb.me/react-with-addons-0.11.2.js"></script>
    <script src="http://fb.me/JSXTransformer-0.11.2.js"></script>
    
    <div id="app"></div>
    
    <script type="text/jsx">
    /** @jsx React.DOM */
    
    var Parent = React.createClass({
      getInitialState: function() {
        return { text: "" };
      },
    
      render: function() {
        return (
          <div>
            <Child onChange={this.updateText} />
            <div><button onClick={this.handleClick}>Alert Text</button></div>
          </div>
        );
      },
      
      handleClick: function() {
        alert(this.state.text);
      },
    
      updateText: function(text) {
        this.setState({text: text});
      }
    });
    
    var Child = React.createClass({
      render: function() {
        return <textarea onChange={this.handleChange} />;
      },
    
      handleChange: function(evt) {
        this.props.onChange(evt.target.value);
      }
    });
    
    React.renderComponent(<Parent />, document.getElementById("app"));
    </script>

    或在孩子中公开一个公共 API:

    <script src="http://fb.me/react-with-addons-0.11.2.js"></script>
    <script src="http://fb.me/JSXTransformer-0.11.2.js"></script>
    
    <div id="app"></div>
    
    <script type="text/jsx">
    /** @jsx React.DOM */
    
    var Parent = React.createClass({
      render: function() {
        return (
          <div>
            <Child ref="child" />
            <div><button onClick={this.handleClick}>Alert Text</button></div>
          </div>
        );
      },
      
      handleClick: function() {
        alert(this.refs.child.getText());
      }
    });
    
    var Child = React.createClass({
      render: function() {
        return <textarea />;
      },
    
      getText: function() {
        return this.getDOMNode().value;
      }
    });
    
    React.renderComponent(<Parent />, document.getElementById("app"));
    </script>

    或使用其他一些数据流管理(例如通量、事件等)。

    【讨论】:

    • “托管”表单元素方法(您的顶级示例)虽然前期成本较高。
    • 嗨,我想知道哪种方式更好(我认为是第一种方式)以及为什么。实际上,为什么我们要避免引用和使用状态。我看到很多关于 SO 的帖子都这么说,但我并不完全理解。谢谢。
    • 现在ref 属性的首选值是回调(请参阅ref Callback Attribute docs),显示访问后代refs 的示例会产生误导。这可能会在未来版本的 React 中中断。
    【解决方案2】:

    最好将ref 视为callback attribute,不再依赖refs 对象。如果您确实使用了refs 对象,请避免访问后代组件的refs。您应该将 refs 视为私有访问器,而不是组件 API 的一部分。仅将组件实例上公开的方法视为其公共 API。

    对于这种情况,我建议从提交事件中获取表单并根据需要遍历其子表单元素。添加name 属性,因为这是在标准表单提交中识别表单元素的方式,然后您根本不需要refs

    var ActivityForm = React.createClass({
      handleSubmit: function(e) {
        e.preventDefault();
        var form = e.target;
    
        // Use the standard [`HTMLFormElement.elements`][1] collection
        //
        // [1]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements
        var content = form.elements['content'].value;
    
        // do more things with other named form elements
      },
      render: function() {
        return (
          <div className="new-activity">
            <h3>New Activity</h3>
            <form onSubmit={this.handleSubmit}>
              <textarea name='content' />
              <br />
    
              <label>Category</label>
              <ActivityFormCategoryDropdown />
              <br />
    
              <input type="submit" value="Add Activity" />
            </form>
          </div>
        );
      }
    });
    

    2016 年 9 月 21 日更新:根据ref String Attribute 文档中的指导,修改建议以避免refs 对象。

    【讨论】:

    • 旁注 - 此行:var form = e.target.getDOMNode(); 应为 var form = e.target;
    • 使用查询选择器的负面影响,如果关注的是 refs,请使用 onChange。
    • @dirkdig 同意querySelector。这是一个旧答案,我不确定为什么我认为这是个好主意。
    • @RossAllen ,也许你可以告诉我。什么时候可以使用 refs?
    • @dirkdig 对于“何时使用 refs”没有简单的答案,但通常有一种比通过 refs 更易于维护的方式来引用组件。 stackoverflow.com/a/29504636/368697 是一个很好的阅读起点,React 在 Refs and the DOM 上的文档也很有价值。
    猜你喜欢
    • 1970-01-01
    • 2021-01-16
    • 2016-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多