从React子元素获取DOM节点

75

使用在v0.13.0中引入的React.findDOMNode方法,我可以通过映射this.props.children获取传递给父组件的每个子组件的DOM节点。

但是,如果某些子组件是React元素而不是组件(例如,通过JSX创建的<div>),则React会抛出不变式违规错误。

无论子类是什么类,有没有一种方法在挂载后获取每个子组件的正确DOM节点?

6个回答

73

this.props.children 应该是一个 ReactElement 或者一个包含 ReactElement 的数组,而不是组件。

如果想获取子元素的 DOM 节点,你需要先克隆它们并为其分配新的 ref。

render() {
  return (
    <div>
      {React.Children.map(this.props.children, (element, idx) => {
        return React.cloneElement(element, { ref: idx });
      })}
    </div>
  );
}

你可以通过this.refs[childIdx]访问子组件,通过ReactDOM.findDOMNode(this.refs[childIdx])检索它们的DOM节点。


4
这不会破坏在父组件中分配给子组件的任何现有 ref 吗?换句话说,如果您的子组件是 <div ref="a" />,那么这不会破坏对 refs.a 的任何引用,因为它被覆盖为 idx 吗? - Aaron Beall
5
可以的。如果您希望保留引用,应使用回调引用{ ref: node => this.node = node; element.ref(node); } - Alexandre Kirszenberg
2
你如何从高阶组件中暴露 ref?例如,export default withInputMask(withRequired(withReadOnly(withMinMax(withHidden(TextInput))))); 如果我想从 withInputMask 访问 TextInput 中的 DOM 元素,该怎么做?我已经尝试了回调 refs,但在 withInputMask 中无法使用。它是未定义的。 - monkeyjumps
@monkeyjumps componentDidMount() { let hoc的最外层DOM节点 = ReactDOM.findDOMNode(this)) } - basil
5
注意,this.props.children 可能包含纯文本节点(在设计可包装任何有效jsx的灵活的 <Tooltip> 组件时,我有时会这样做)。这将导致在 React.cloneElement(element) 中出现错误。因此,最好检查 React.isValidElement(element) 和/或 typeof element === 'string' - V. Rubinetti
值得注意的是,这对于功能组件是不起作用的。 - undefined

18
如果想访问任何DOM元素,只需添加ref属性即可直接访问该元素。
<input type="text" ref="myinput">

然后您可以直接:

componentDidMount: function() 
{
    this.refs.myinput.select();

},

如果您已经为任何元素添加了ref,则无需使用ReactDOM.findDOMNode()


8
请注意,只有在向DOM元素添加ref(例如<div ref="myRef" />)时,您才不需要使用findDOMNode。如果您在React组件节点上使用ref(例如<MyComponent ref="myRef" />),则需要使用它。在第一种情况下,this.refs.myRef是DOM节点,而在第二种情况下,它是React组件实例。 - fabio.sussetto

18

通过使用“refs”属性可能可以实现这一点。

以想要到达一个<div>为例,你需要使用的是<div ref="myExample">。然后,您就可以使用React.findDOMNode(this.refs.myExample)获取该DOM节点。

从那里开始,获取每个子元素的正确DOM节点可能只需要映射this.refs.myExample.children(我还没有测试),但您至少可以通过使用ref属性来抓取任何特定的挂载子节点。

有关更多信息,请参见官方react关于refs的文档


对我来说有效,但现在的API是ReactDom.findDOMNode(..) - Ben Winding
当你走上设置组件状态的反模式路径时,这是一个极好的解决方案(如果你遇到了这个问题,解决方案是不要使用这种模式,而是通过有状态的变量进行条件渲染)。 - fungusanthrax

9
你可以使用新的React ref api来实现这一点。
function ChildComponent({ childRef }) {
  return <div ref={childRef} />;
}

class Parent extends React.Component {
  myRef = React.createRef();

  get doSomethingWithChildRef() {
    console.log(this.myRef); // Will access child DOM node.
  }

  render() {
    return <ChildComponent childRef={this.myRef} />;
  }
}

1
这是截至2018年11月最新且最简单的答案。 - olive_tree
3
如何为未知数量的“children”进行适配? - Sumit

5

React.findDOMNode(this.refs.myExample)在另一个答案中提到已被弃用。

请使用'react-dom'中的ReactDOM.findDOMNode代替。

import ReactDOM from 'react-dom'
let myExample = ReactDOM.findDOMNode(this.refs.myExample)

3
我发现使用新的回调引用(callback refs)方法很简单。你可以将一个回调函数作为属性(prop)传递给子组件。像这样:

class Container extends React.Component {
  constructor(props) {
    super(props)
    this.setRef = this.setRef.bind(this)
  }

  setRef(node) {
    this.childRef = node
  }

  render() {
    return <Child setRef={ this.setRef }/>
  }
}

const Child = ({ setRef }) => (
    <div ref={ setRef }>
    </div>
)

这里有一个使用模态框实现此操作的示例:
class Container extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      modalOpen: false
    }
    this.open = this.open.bind(this)
    this.close = this.close.bind(this)
    this.setModal = this.setModal.bind(this)
  }

  open() {
    this.setState({ open: true })
  }

  close(event) {
    if (!this.modal.contains(event.target)) {
      this.setState({ open: false })
    }
  }

  setModal(node) {
    this.modal = node
  }

  render() {
    let { modalOpen } = this.state
    return (
      <div>
        <button onClick={ this.open }>Open</button>
        {
          modalOpen ? <Modal close={ this.close } setModal={ this.setModal }/> : null
        }
      </div>
    )
  }
}

const Modal = ({ close, setModal }) => (
  <div className='modal' onClick={ close }>
    <div className='modal-window' ref={ setModal }>
    </div>
  </div>
)

1
这真的很棒。我找了一段时间关于如何做到这一点。它很简单,像魔法一样工作,并且不涉及使用hacky React.findDOMNode业务。 - Lauren
关于您的编辑建议,这些只适用于使用ES7的情况,而我尚未为webpack进行配置。 - Wylliam Judd
这样使用旧语法的人仍然能够理解这个答案。 - Wylliam Judd
1
真正使用这个的人会希望在它周围看到。 (为了上下文:我建议使用箭头函数来绑定this,而不是在构造函数中执行。)作为后续,我发现另一种获得子引用的方法,我在下面记录为解决方案。 - Lauren

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