JSX in Depth

JSX is just JavaScript. It just happens to look like HTML. A JSX tag is a function that creates an HTML tag with attributes and children.

Let's take an example:

// functional component:
function Title(title) {
  return (
    

{title}

) } // The functional component as a custom JSX tag: <Title {Hello, world!}/> // The same tag as a hyperscript function: h('h1', {title: 'The title'}, 'Hello, world!') // The tag produces the following virtual node: { type: "h1", props: {title: "The title"}, children: ["Hello, world!"] } // The virtual node gets converted into a DOM node: <h1>Hello, world!</h1>

JSX is just a simplier way to represent the DOM nodes your component needs to create. Because it is JavaScript, you can use it with normal JavaScript expressions to evaluate and parse the data that you need to display.

Composition

Although you can define simple JSX tags, the real strength of JSX is that it lets you you simple JSX elements combined together to create more complex ones. These parts can even be in separate files, allowing for their reuse through your app or in other apps.

As an example of composition, let's create a dialog box. We are going to start with the dialog itself, which is just the border around the content:

function FancyBorder(props) {
  console.log(props)
  return (
    <div class='FancyBorder FancyBorder-maroon'></div>
  )
}
// Inject the dialog in the document:
mount(<FancyBorder />), 'body'

We'll update this to allow the user to pass in the color as a prop:

function FancyBorder(props) {
  console.log(props)
  return (
    <div class={'FancyBorder FancyBorder-' + props.color}></div>
  )
}
// Inject the dialog in the document:
mount(<FancyBorder color="maroon" />,'body')

So, now we have a very basic function component. But we want to be able to render it with a child component. Here is the child:

function DialogMessage() {
  return (
    <div>
      <h1 class="Dialog-title">Welcome</h1>
      <p class="Dialog-message">Thank you for visiting our time machine!</p>
    </div>
  )
}

Now, the question is, how can we get the child component into the dialog box? As you saw above, we passed the color to the dialog box as a prop. We can do the same with our child component. We'll make a slight modification to FancyBorder so that it can received arbitrary child nodes. We'll do that by passing a second parameter to the function, which we'll call children:

function FancyBorder(props, children) {
  console.log(props)
  return (
    <div class={'FancyBorder FancyBorder-' + props.color}>
      {children}
    </div>
  )
}

With the FancyBorder component set to capture a children prop, we can pass in our DialogMessage component when we render FancyBorder:

See the Pen Composition by Robert Biggs (@rbiggs) on CodePen.

Through composition we are able to add child components to other components to build more complex solutions.

Composition with Class Components

We can use this same technique with class components. We are going to show how to use composition of JSX functional components to create a class component. We are going to start with the same FancyBorder component we made earlier:

function FancyBorder(props, children) {
  console.log(props)
  return (
    <div class={'FancyBorder FancyBorder-' + props.color}>
      {children}
    </div>
  )
}

We'll use this as the base for our new component. We want to have a dialog where the user can enter a name, and after tapping a button, gets a greeting. By the way, this will be a signup for the Time Travel Program.

Issues to Deal With

Because we are going to be passing props down from a class component to child components that are function, there will be some wrapping of the props at each level. Kind of annoying, but its what JSX does in this situation. Therefore we will need to use .props to get at values in places where you would expect too.

Here's the basic setup for our class component:

class SignUpDialog extends Component {
  constructor(props) {
    super(props)
    this.container = 'section'
    this.state = {
      login: '',
      title:"Time Travel Program",
      message:"How should we refer to you?"
    }
  }

  render(props) {
    return (
        return (
      <FancyBorder color='maroon'>
       <DialogChildren {...{title: props.title, message: props.message, component: this}}/>
      </FancyBorder>
    )
  }
}

So, we have a class component that returns our FancyBorder component, while passing it props for color and children. Notice how dialogChildren is get passed props and this. That will pass a reference of the class component down to the children so they have access to the parent's properties. In particular we want to expose the parent's state and event methods to the children.

We'll need a component to create the default title and greetign for the dialog. We'll call that component Dialog. We'll also need a component to be the form where the user enters there name an taps a button to submit it. We'll call that FormInputs. Then we'll create a function called dialogChildren which will combine these to. We'll pass this function to the FancyBorder component as its children prop. And, we will add some methods and events to make name submission work. Here is the complete solution:

See the Pen Composition 2 by Robert Biggs (@rbiggs) on CodePen.

As you can see, with some work you can use JSX composition to advantage, even inside a class component.

Containement

Another pattern for JSX development is container, where on component contains another. In our previous example we used props to pass in a function that composed several JSX function components together. We can also pass in custom JSX tags for a similar composition pattern. Take a look at the following example. Notice how we have our custom tags defined: <Contacts /> and <Chat />, and that we pass them to the SplitPane tag as its value for left and right:

See the Pen Containment by Robert Biggs (@rbiggs) on CodePen.