Inject custom data attribute to REACT `props.children`

Viewed 1528

I have a functional component that only render props.children, but I'd like to inject some custom data attribute to the DOM element that it renders.

Let's look at an example:

const Decorator = props => {
    return props.children;
}

const Child = props => {
    return <div>I'm a child</div>
}

- - -

// Suppose we have this:
<Decorator><Child /></Decorator>

// Then, the DOM output will be:
<div>I'm a child</div>

Now, from the example above what I'd like to achieve is that any DOM element rendered via Decorator has a custom data attribute, so:

// Suppose we have this:
<Decorator><Child /></Decorator>

// Then, the DOM output will be:
<div data-decorator-attr="foo">I'm a child</div>

I've searched here and there, but I haven't found a way to do this. Here's the list of possibilities that I have thought about and the reasons why they cannot be used:

  • Inside <Decorator> I could use React.cloneElement and inject a ref to props.children, but the ref would reference the component itself, and NOT the rendered DOM. I would need to export Child element using React.forwardRef but this is not viable (that's because <Decorator> could wrap anything, and I cannot edit all other component to be React.forwardRef;
  • Being a functional component, I can't even use React.findDOMNode using the reference to the component itself;
  • Inside <Decorator>, I cuold wrap props.children with a <Fragment>, but <Fragment> doesn't support ref;

So.. I have no idea how I could achieve this. The only way I found out is to renders a wrapper DOM element in the <Decorator>, like this:

const Decorator = props => {
    return <div style={{display: 'contents'}}>{props.children}</div>;
}

But what I wanted was to NOT have any wrapper DOM element.

1 Answers

I don't know if this helps:

const Decorator = props => {
  const elements = React.Children.toArray(props.children).map(
    (element) => React.cloneElement(element, { 'data-decorator-attr': 'foo' })
  );
  
  return <React.Fragment>{elements}</React.Fragment>;
};

const Child = props => <div {...props}>I'm a child</div>;

const App = () => <Decorator><Child /><div>Just a div</div></Decorator>;

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
<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="root"></div>

I used https://frontarm.com/james-k-nelson/passing-data-props-children/ as a reference.

Related