React.js: What’s this.props.children in JSX all about?

November 12, 2017 0 By Pavan Tumati

If you’re expecting the second installment of the post on Server side swift, don’t worry.  I’m still documenting some things!

Lately I’ve been toying with the idea of converting an old UI written in Qt to make it work with React, and React is new to me.  In React, the user interface is defined in terms of JSX, where JSX is this markup language that eventually transforms a user interface definition (of sorts) into usable JavaScript.  There’s just one minor issue:  Transpiling often obscures what goes on under the hood.  Normally, I learn things fastest by reading other people’s code; however, with React, I find it more beneficial to read other people’s code AND read what the JSX is transpiled into.

In my case, I was a little bit confused by what was happening under the hood with regards to JSX and “this.props.children”, at least from skimming documentation and trying to crank out something quickly (at the expense of being thorough.)

More specifically, I was curious what got generated under the hood when a situation like the following was encountered:

<SomeReactComponent>
    <SomeChildReactComponent/>
    <SomeChildReactComponent/>
<SomeReactComponent />

Normally, after transpiling (via Babel), each React component gets created via React.createElement().  If there were multiple child nodes (like in SomeReactComponent above, where there are two “SomeChildReactComponent” elements), how did react actually handle the element creation?  The documentation and several examples say to use {this.props.children}.  But what is actually happening here?

I figured I’d construct an example and transform it via Babel to see what it looked like. I have an EncloserApp top level component. Inside the top level component, I created an Encloser. Encloser then “encloses” EnclosedElement. The objective was to just see what the transpiled output looks like:

class Encloser extends React.Component {
    render() {
        const borderedStyle = {border: "1px solid red", padding: 6};
        return (
            <div style={borderedStyle}>
            {this.props.children}
            </div>
        )
    }
}

class EnclosedElement extends React.Component {
    static getPropTypes() {
        return {
            "custom_string_property" : React.PropTypes.string.isRequired
        }
    }
    render() {
        return (
            <h1>{this.props.custom_string_property}</h1>
        )
    }
}

class EncloserApp extends React.Component {
    render() {
        return (
            <Encloser>
                <EnclosedElement custom_string_property="String One"/>
                <EnclosedElement custom_string_property="String Two"/>
            </Encloser>
        )
    }
}

const contentNode = document.getElementById( 'contents' );
ReactDOM.render( 
    (
        <div>
            <EncloserApp />
        </div>
    )
    , contentNode
);

In the transpiled code, as expected, the top level element is created as such:

var contentNode = document.getElementById('contents');
ReactDOM.render(React.createElement(
    "div",
    null,
    React.createElement(EncloserApp, null)
), contentNode);

The createElement() call creates the div, as expected, passes along no properties (null), and then creates a singular child node for the EncloserApp.  Now, inspecting EncloserApp, the code (around the render() method) looks like this:

 

_createClass(EncloserApp, [{
        key: "render",
        value: function render() {
            return React.createElement(
                Encloser,
                null,
                React.createElement(EnclosedElement, { custom_string_property: "String One" }),
                React.createElement(EnclosedElement, { custom_string_property: "String Two" })
            );
        }
    }]);

So, essentially, {this.props.children} is transformed into a list of child nodes that are passed as parameters to createElement (which accepts a variable number of arguments.  “this.props.children” exists in JSX specifically for JSX markup that exists as an opening and closing tag (at least according to the documentation.)

I know this is not an earth shattering blog post, but it helped me to see the nature of the transformation underneath.  The system makes more sense seeing the transformation of JSX into the graph containing React component nodes.

Ah, clarity.