React:创建和引用动态组件引用

3

我想要跟踪任意数量的子组件。

通常,您只需引用this.refs.refName,但在我的情况下,我需要跟踪任意数量的refs。

这里是一个简洁的例子:

var NamesList = React.createClass({
  getAllNames: function() {
    // Somehow return an array of names...

    var names = [];
    this.refs.????.forEach(function (span) {
      names.push(span.textContent);
    })
  },

  render: function() {
    var names = ['jack','fred','bob','carl'];
    var spans = [];

    names.forEach(function (name) {
      spans.push(<span contentEditable={true} ref='?????'>name</span>);
    });

    return (
      <div>
        {spans}
      </div>;
    );
  }
});

ReactDOM.render(<NamesList></NamesList>, mountNode);

如果我在处理问题时有误,请让我知道。我的期望结果是从RESTful服务传递数据到一个React组件中,允许用户编辑这些数据,并在需要时再次导出。我无法在React refs文档中找到答案。

3个回答

2
以下代码将每个span的引用保存在名为this.spans的数组中。
这是因为每个span都使用addSpanRef方法将自己添加到refs数组中。 getAllNames然后遍历所有引用的span并获取它们的textContent。
如果您有子组件(而不是spans),则可以对这些子节点调用方法!
var NamesList = React.createClass({

  // returns an array of names
  getAllNames() {
    return this.spans.map((span) => span.textContent);
  },

  // Add to our spans refs array
  addSpanRef (node) {
    this.spans = [...this.spans, node];
  },

  render: function() {
    this.spans = []; // create the spans refs array
    var names = ['jack','fred','bob','carl'];

    // Save a ref to "this" for the forEach loop
    var thisRef = this;
    names.forEach(function (name) {
      spans.push(<span contentEditable={true} ref={thisRef.addSpanRef}>name</span>);
    });

    return (
      <div>
        {spans}
      </div>;
    );
  }
});

ReactDOM.render(<NamesList></NamesList>, mountNode);

1

1. 快速而简单的解决方案:通过容器组件上的 ref 读取名称

读取所有名称的一种方法是在容器上放置一个 ref,并在 childNode spans 上读取 textcontent。

  componentDidMount: function() {
    var elem = this.refs.container.getDOMNode();
    var nameNodes = elem.children;
    var names = [];
    for (var i=0; i<nameNodes.length; i++) {
      names.push(nameNodes[i].textContent);
    }
    console.log(names);
  },

你可以在这里找到代码示例

警告:以上代码有点混乱:用户可以更改span的内容,而React不知道DOM发生了变化。这是一个非常糟糕的想法

2. 更清晰的解决方案

所以你需要状态:用户可以更改span的内容,而React需要知道这一点。
因为你还需要一个所有(新)编辑过的名称的数组,所以此状态需要驻留在容器级别。
这里提供了一个使用纯<EditableSpan>组件的解决方案,每当名称更改时,它们都会调用其父组件上的方法。

var EditableSpan = React.createClass({
  onChange: function() {
    var newContent = event.target.textContent;
    this.props.onChange(this.props.index, newContent);
  },
  render: function() {
    return <span 
             contentEditable={true} 
             onInput={this.onChange}>
      {this.props.name}</span>
  }
});

请注意,这个更干净的解决方案不再使用 refs,因为它们是不必要的:所有数据都已知于 React,所以 React 不需要 refs 从 DOM 中读取。相反,它在每次更改时读取 event.target 来更新容器状态。
您可以在此处找到完整的工作代码 codepen
注意:对于这个解决方案,我添加了快速而简单的键(名称)。React 需要唯一的键(而不是索引)。由于列表中的名称不能保证是唯一的,因此您可能需要另一个更智能的解决方案(例如创建的 ID 或时间戳)。

抱歉,我正在尝试动态地从span中提取名称。看起来你所做的更改是关于动态设置span的。伪代码如下:对于每个span,获取textContent并将其作为数组返回。这有助于解释我的问题吗? - joshuakcockrell
更新了答案,提供了两种解决方案。请注意,第二种解决方案确实返回一个包含span元素textContent的数组,但是没有使用refs。React应该是并且必须是textContent的唯一所有者和管理者。因此,使用refs获取名称不是必要的(也会导致代码出错/难以管理)。 - wintvelt

-1

这可能是我发现的创建动态引用最简单的方法。

此外,这个解决方案在 TypeScript 中也可以无障碍使用。

const Child = props => <input ref={refElem => setRef(props.someKey, refElem)} />

class Parent extends Component {

    setRef = (key, ref) => {
      this[key] = ref; // Once this function fires, I know about my child :)
    };

    render(){
        return (
          {myList.map(listItem => <Child someKey={listItem.key} setRef={this.setRef} />)}
        )
    }
}

希望这能帮助到某个人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接