MarksCode
MarksCode

Reputation: 8594

ReactDOM.findDOMNode() using React.Children child

I'm trying to access a component's specific child DOM node like this:

let targetChild = null;
React.Children.forEach(children, child => {
    if (child.type.displayName === 'TargetElement') {
        targetChild = child;
    }
});

const targetEl = ReactDOM.findDOMNode(targetChild);

However, I keep getting the error:

Uncaught Error: Argument appears to not be a ReactComponent.

When I log out the typeof targetChild I get object. Does anyone know how to fix this? Thanks!

Upvotes: 2

Views: 3738

Answers (2)

Vikram Deshmukh
Vikram Deshmukh

Reputation: 15656

In order to get the DOM element of a particular React component instance, you need to use the Callback Refs.

Here's a demo for you:

const children = ['Bob', 'Jeff', 'Kate'];
const TARGET_NODE = 'Jeff';
let jeffRef;

const setRef = (el, label) => {
	if(label === TARGET_NODE) {
		jeffRef = el;
	}
}

const SampleApp = ({children}) => (
	<ul>
  	{children.map((child, index) => (
	    	<li key={index} ref={el => setRef(el, child)}>{child}</li>
	))}
	</ul>
)

ReactDOM.render(<SampleApp children={children}/>, document.querySelector("#app"))

console.log(jeffRef)	//	Should output: <li>Jeff</li>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

PS: In my opinion, accessing DOM elements can be avoided in most cases. You might want to reconsider doing that. You can control/update the element using component state instead.

Upvotes: 0

Tripurari Shankar
Tripurari Shankar

Reputation: 3558

Above all findDOMNode should not be used

Also note that typeof null is object

The problem is you are passing the React Element instead of React Component's instance

read more about difference between React Element vs React Component vs React Component instance https://reactjs.org/blog/2015/12/18/react-components-elements-and-instances.html

in short if UL is a React Component ( either class component or function component ) then <UL /> is React element

Also note that you can use findDOMNode only on Class Component not the function component

The documentation seems a little misleading because it says

ReactDOM.findDOMNode(component)

notice it says Component but I did some search and find out that you need to pass the component's instance instead of component

for example following will work because this will points to the component's instance

import React  from "react";
import ReactDOM from "react-dom";

const UL = props => {
  return <ul>{props.children}</ul>;
};

class List extends React.Component {
  componentDidMount() {
    console.log('printing', ReactDOM.findDOMNode(this));
  }
  render() {
    return <li>this is targe element</li>;
  }
}

export default function App() {
  return (
    <div className="App">
      <UL>
        <li>Hello CodeSandbox</li>
        <List />
        <li>Start editing to see some magic happen!</li>
      </UL>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

but following will not work because we are passing a React element instead of component's instance

import React from "react";
import ReactDOM from "react-dom";

class UL extends React.Component {
  componentDidMount() {
    let element = null;
    React.Children.forEach(this.props.children, (child, index) => {
      if (index === 1) {
        console.log('logging child', child);
        element = child;
      }
    });
    console.log('printing', ReactDOM.findDOMNode(element));
  }
  render() {
    return <ul>{this.props.children}</ul>;
  }
};

class List extends React.Component {
  render() {
    return <li>this is targe element</li>;
  }
}

List.displayName = 'TargetElement';

export default function App() {
  return (
    <div className="App">
      <UL>
        <li>Hello CodeSandbox</li>
        <List />
        <li>Start editing to see some magic happen!</li>
      </UL>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Upvotes: 3

Related Questions