Reputation: 783
I have a very annoying issue, and I need help with my component. It is used in such context:
<table>
<thead>/* ... */</thead>
<tbody>
<COMPONENT>
<ChildComponent>/* ... */</ChildComponent>
<ChildComponent>/* ... */</ChildComponent>
<ChildComponent>/* ... */</ChildComponent>
</COMPONENT>
</tbody>
</table>
ChildComponent is a component that includes other components but eventually renders simple HTML's <tr>
inside component.tsx, I need to get DOM values (offsetTop and clientHeight) for n-th child.
I've tried many things:
ReactDOM.findDOMNode(children[n])
gives me:argument appears to not be a ReactComponent. Keys: $$typeof, type, key, ref, props, _owner, _store
children[n].ref
just gives me nullchildren.map((child, index) =>
index === n ? (
<div style={{display: contents}} key={index} ref={ref}>
) : child
)
gives me warning (but works!):
index.js:2178 Warning: validateDOMNesting(...): cannot appear as a child of . in tr (created by FoldControlContainer) ...
is there a better solution? I've tried to use <>, or other "transparent" to DOM components instead of div
, but it didn't work.
Upvotes: 2
Views: 7070
Reputation: 222309
findDOMNode
expects an instance of React.Component
. Components should be rendered with refs in order to get instances. Since refs aren't used in <ChildComponent>/* ... */</ChildComponent>
children, they should be cloned with a ref.
If children are supposed to be class component elements and their amount doesn't change during parent component lifespan, the case can be simplified to:
childRefs = [...Array(React.Children.count(this.props.children))]
.map(() => React.createRef());
render() {
return React.Children.map(this.props.children, ((child, i) =>
React.cloneElement(child, { ref: this.childRefs[i] });
)
}
DOM element is available as ReactDOM.findDOMNode(this.childRefs[n].current)
when the component is mounted.
Upvotes: 2
Reputation: 616
use React.cloneElement to set the refs for the children: https://reactjs.org/docs/react-api.html#cloneelement ex:
React.cloneElement(child, {ref: this.setChildRef})}
export default class Test extends React.Component {
childrenRefs = {}
setChildRef = index => el => this.childrenRefs[index] = el;
showInfo = () => {
console.log('children', Object.keys(this.childrenRefs).map(key => this.childrenRefs[key].offsetTop))
}
render() {
return (
<div>
{ React.Children.toArray(this.props.children).map((c, index) => React.cloneElement(
c,
{ref: this.setChildRef(index)},
))}
<button onClick={this.showInfo} >test</button>
</div>
);
}
}
here is a link to a full working example: https://stackblitz.com/edit/react-cf33ge Open the console at the bottom right to see the output.
Upvotes: 4