74

Using the React.findDOMNode method that was introduced in v0.13.0 I am able to get the DOM node of each child component that was passed into a parent by mapping over this.props.children.

However, if some of the children happen to be React Elements rather than Components (e.g. one of the children is a <div> created via JSX) React throws an invariant violation error.

Is there a way to get the correct DOM node of each child after mount regardless of what class the child is?

Graham Conzett
  • 8,344
  • 11
  • 55
  • 94

6 Answers6

74

this.props.children should either be a ReactElement or an array of ReactElement, but not components.

To get the DOM nodes of the children elements, you need to clone them and assign them a new ref.

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

You can then access the child components via this.refs[childIdx], and retrieve their DOM nodes via ReactDOM.findDOMNode(this.refs[childIdx]).

Alexandre Kirszenberg
  • 35,938
  • 10
  • 88
  • 72
  • 4
    Doesn't this break any existing `ref` that was assigned to the children in the parent? In other words if you had `
    ` as your children, wouldn't this break any references to `refs.a` because you overwrite it to `idx`?
    – Aaron Beall Feb 08 '16 at 23:23
  • 5
    It does. If you wish to preserve refs, you should use [callback refs](https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute). `{ ref: node => this.node = node; element.ref(node); }` – Alexandre Kirszenberg Feb 09 '16 at 15:52
  • 2
    how do you expose the ref from a higher order component? For example, export default withInputMask(withRequired(withReadOnly(withMinMax(withHidden(TextInput))))); if I want access to a dom element in TextInput from withInputMask, how would i achieve it? I have tried callback refs but its not made available in withInputMask. Its undefined. – monkeyjumps Aug 09 '17 at 16:15
  • @monkeyjumps `componentDidMount() {` `let outtermostDomNodeOfHoc = ReactDOM.findDOMNode(this))` `}` – basil Apr 01 '18 at 21:12
  • 4
    Note that `this.props.children` can contain pure text nodes (as it sometimes did in my case when designing a flexible `` component that could be wrapped around any valid jsx). This will cause an error in `React.cloneElement(element)`. As such, it's worthwhile to check `React.isValidElement(element)` and/or `typeof element === 'string'`. – V. Rubinetti Apr 19 '19 at 14:31
19

If you want to access any DOM element simply add ref attribute and you can directly access that element.

<input type="text" ref="myinput">

And then you can directly:

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

},

Their is no need of using ReactDOM.findDOMNode(), if you have added a ref to any element.

adnan kamili
  • 8,967
  • 7
  • 65
  • 125
  • 8
    Note that you don't need findDOMNode only if you have added a ref to a DOM element (e.g.
    ). You do need it if you use ref on a React component node (e.g. . In the first case this.refs.myRef is the DOM node, while in the second it's the React component instance.
    – fabio.sussetto Oct 25 '16 at 10:42
18

This may be possible by using the refs attribute.

In the example of wanting to to reach a <div> what you would want to do is use is <div ref="myExample">. Then you would be able to get that DOM node by using React.findDOMNode(this.refs.myExample).

From there getting the correct DOM node of each child may be as simple as mapping over this.refs.myExample.children(I haven't tested that yet) but you'll at least be able to grab any specific mounted child node by using the ref attribute.

Here's the official react documentation on refs for more info.

Pavan Ravipati
  • 1,890
  • 14
  • 21
  • Worked for me, but the api is now `ReactDom.findDOMNode(..)` – Ben Winding Jul 19 '17 at 05:50
  • excellent solution when going down the anti-pattern path of setting components in state (the solution if you have this is to not do this pattern and conditionally render it by a stateful variable) – fungusanthrax Jun 21 '18 at 17:03
9

You can do this using the new 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} />;
  }
}
Andi
  • 3,249
  • 1
  • 20
  • 12
Lauren
  • 2,557
  • 1
  • 14
  • 16
5

React.findDOMNode(this.refs.myExample) mentioned in another answer has been deprectaed.

use ReactDOM.findDOMNode from 'react-dom' instead

import ReactDOM from 'react-dom'
let myExample = ReactDOM.findDOMNode(this.refs.myExample)
bhaskarc
  • 9,269
  • 10
  • 65
  • 86
3

I found an easy way using the new callback refs. You can just pass a callback as a prop to the child component. Like this:

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>
)

Here's an example of doing this with a modal:

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>
)
Wylliam Judd
  • 9,935
  • 2
  • 26
  • 36
  • 1
    This is really slick. I looked around for a while on how to do this. It's simple, works like a charm and doesn't involve using hacky React.findDOMNode business. – Lauren Sep 13 '18 at 20:43
  • Regarding your edit suggestions those only work if you're using ES7, which I haven't configured webpack for yet. – Wylliam Judd Sep 14 '18 at 20:57
  • This way people who are using older syntax will still be able to figure out this answer. – Wylliam Judd Sep 14 '18 at 20:58
  • 1
    True and those that use this will hopefully see around it. (For context: I suggested using an arrow function to bind `this` instead of doing it in the constructor.) As a follow-up, I did find that another way to gain access to a child ref, I documented as a solution below. – Lauren Sep 20 '18 at 00:52