Rotareti Rotareti - 4 months ago 24
Javascript Question

How do I get the dom node of a child element in a component without adding logic to its parent/children?

In the following example

WrapperComp
needs to get access to the dom node of the
divs
in line 5 and line 8, without adding logic to
PageComp
or
ItemComp
. The only things I could change in
PageComp
are the div tags. E.g. I could add a
ref
,
prop
,
data-attribute
, etc to them.

The
divs
don't have to be created inside
PageComp
.
WrapperComp
would be allowed to create them too, but they must wrap each of its children (In this case each
ItemComp
).

Example

class PageComp extends React.Component {
render() {
return (
<WrapperComp>
<div>
<ItemComp/>
</div>
<div>
<ItemComp/>
</div>
</WrapperComp>
);
}
}

class WrapperComp extends React.Component {
render() {
return (
<div>
<h1>A wrapper</h1>
{this.props.children}
</div>
);
}
}

class ItemComp extends React.Component {
render() {
return (
<div>
<h1>An item</h1>
</div>
);
}
}

ReactDOM.render(
<PageComp/>,
document.getElementById('app')
);


JSBIN

What I tried so far:


  • I already tried to put a
    ref=
    on the
    divs
    , but that
    ref
    would only be available in
    PageComp
    not in
    WrapperComp
    .

  • I also tried to create the
    divs
    inside
    WrapperComp
    and put a
    ref=
    on them from there, but that would result in a Refs Must Have Owner Warning



Now I wonder.. what would be an appropriate way in react to solve that problem?

Till now the only solution that came to my mind was to put a
data-attribute
on each
div
and search the dom for them after
componentDidMount
like that:
document.querySelectorAll('[data-xxx]')
. Perhaps I'm not sure if this is how you do it in react..

Why do I want to get the node inside
WrapperComp
?

I want to create a component that adjusts the dimensions of its children. In the example that component would be
WrapperComp
. The adjustments can only be done after the children rendered to the dom, e.g. to get
clientHeight
.

Answer

If you don't restrict that this needs to be solved by how one should get the DOM, pass them down, etc, I would get rid of the puzzle and approach it in a different direction.

Since you are not given much control to <PageComp> whereas <WrapperComp> seems flexible, I would do the wrapping in the later by transforming the passed children to what you need them to be.

class PageComp extends React.Component {
  render() {
    return (
      <WrapperComp>
        <ItemComp/>
        <ItemComp/>
      </WrapperComp>
    );
  }
}

class WrapperComp extends React.Component {
  render() {
    const wrappedChldren = React.Children.map(this.props.children, function(child) {
      return (
        <div ref={function(div) {
            this.setState{clientHeight: div.clientHeight}
        }}>
          <h1>A wrapper</h1>
          { child }
        </div>
      ); 
    });
    return <div>{ wrappedChildren }</div>;
  }
}

With this concentrate can be put on the transformation in the <WrapperComp>, which is pretty intuitive as its name suggests.