Search code examples
reactjscomponentsrefjsx

Include this.props.children as a component within the jsx


I am very new to the react world and I am trying to include include this.props.children as a component via React.cloneElement. I am trying this so that I could refer that component using a ref.

For example, lets say I get the children to a const as follows,

const Child = React.cloneElement(this.props.children, this.props);

and try to render it like:

return (
    <div style={styles.container}>
        <Child ref={"child"} />
    </div>
);

But this is throwing this error: warning.js?0260:45 Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method of 'OnlineOrderPane'.

It would be of great help if any of you experts would point me in the right direction to achieve this or if its actually feasible to do so.

Thanks alot.


Solution

  • React.cloneElement only takes a single element, but this.props.children is a collection of elements (regardless of whether you only send in one child).

    So in order to clone these elements you have to iterate over them

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

    This code will populate this.refs so that for each child element you can access it's ref by index, e.g. this.refs[0],this.refs[1] etc... See Example A below.

    If you only expect one element and just want to clone that element, then simply take the first element from the this.props.children collection. But be aware that it is not a simple array, so you must use a React helper method: React.Children.only.

    Then just clone the element and pass any ref you would like to use:

    var onlyChild = React.Children.only(this.props.children);
    var clone = React.cloneElement(onlyChild, { ref: "test" });
    

    See also Example B.

    Example A (Fiddle here)

    var P = React.createClass({
      render: function() {      
        return <div>Parent<br/>
        {React.Children.map(this.props.children, (element, idx) => {
            return React.cloneElement(element, { ref: idx });
        })}
        </div>;
      },
    
      componentDidMount: function(prevProps, prevState) {
        ReactDOM.findDOMNode(this.refs[0]).style.backgroundColor="green";
        ReactDOM.findDOMNode(this.refs[1]).style.backgroundColor="blue";
        ReactDOM.findDOMNode(this.refs[2]).style.backgroundColor="red";
      }
    });
    
    var C = React.createClass({
      render: function() {
        return <div>{this.props.text}</div>;
      }
    });
    
    ReactDOM.render(
      <P>
      <C text="a"/>
      <C text="b"/>
      <C text="c"/>
      </P>,
      document.getElementById('container')
    );
    

    Example B (Fiddle here)

    var P = React.createClass({
      render: function() {
        var onlyChild = React.Children.only(this.props.children);
        var clone = React.cloneElement(onlyChild, { ref: "test" });
        return <div>Parent<br/>
            {clone}
        </div>;
      },
    
      componentDidMount: function(prevProps, prevState) {
        ReactDOM.findDOMNode(this.refs["test"]).style.backgroundColor="green";
      }
    });
    
    var C = React.createClass({
      render: function() {
        return <div>{this.props.text}</div>;
      }
    });
    
    ReactDOM.render(
      <P>
      <C text="a"/>
      </P>,
      document.getElementById('container')
    );
    

    Both examples assume a simple <div id='container'></div> on the page.