39 Syntax Expansion Through Jsx for Syntax Expansion

39 Syntax Expansion Through JSX for Syntax Expansion #

Hello, I’m Ishikawa.

In the previous lesson, we mentioned that React also has a JavaScript syntax extension called JSX. Its full name is JavaScript XML, and as the name suggests, it is designed in XML format. Its main purpose is to be combined with the React framework for web UI development in applications. In React, the DOM tree is defined using JSX and will ultimately be rendered as HTML in the browser. Given the popularity of React, even if you haven’t used React before, you’ve probably heard of JSX.

Today, let’s take a look at how JSX is used in web UI development. Even if you don’t use React, this template pattern has a lot of valuable ideas to borrow.

HTML-like Elements #

First, let’s take a look at the basic usage of JSX and how it creates elements, along with their properties and child elements.

You can think of JSX elements as a new syntax expression in JavaScript. In standard JavaScript, string literals are enclosed by double quotes and regular expression literals are enclosed by slashes. Similarly, JSX literals are enclosed by angle brackets. For example, the

#

tag is a simple example of how we create a React heading element using this syntax.

var title = <h1 className="title">Page Title</h1>;

However, tags like the one above cannot be understood by the JavaScript engine, so how are they executed? In this case, we can use Babel, which we mentioned in the previous lecture, to transpile JSX expressions into standard JavaScript before compiling and executing them in the browser. Babel transpiles the JSX expression in the variable assignment above into a createElement() function that complies with JavaScript specifications, and then calls that function.

var title = React.createElement("h1", {className: 'title'}, "Page Title");

Because React elements created through JSX are similar to HTML tags, JSX-generated React element expressions can also have attributes. When an element has one or more attributes, they are passed as the second parameter to createElement(), with the value being an object containing the element’s attributes.

// Before transpilation
var image = <img src="logo.png" alt="company logo" />;

// After transpilation
var image = React.createElement("img", {
                  src: "logo.png",
                  alt: "company logo"
                });

Just like HTML elements, React elements can also have child elements, in addition to strings. React elements can be nested hierarchically to create a DOM tree. During transpilation, the DOM child elements nested within JSX can be passed as the third and subsequent arguments to the createElement() function call.

// Before transpilation
var sidebar = (
  <div className="sidebar">
    <h2 className="menu">Menu</h2>
    <p className="text">Menu content</p>
  </div>
);

// After transpilation
var sidebar = React.createElement(
        "div", { className: "sidebar"},  
        React.createElement("h1", className: "menu"},  
                            "Menu"),    
        React.createElement("p", className: "text"},   
                            "Menu content"));

The value returned by the createElement() function in React is a JavaScript object, which React uses to render the UI in the browser window. In the example below, we can see a React element expressed through JSX, which is transpiled into an object through createElement(). Then, the object is rendered to the page using the render() method in React.

// Before transpilation
var element = <h1 class"title">Page Title</h1>;

// After transpilation
var element = React.createElement(
  'h1',
  {className: 'title'},
  'Page Title'
);

// Object returned by createElement()
var element = {
  type: 'h1',
  props: {
    className: 'title',
    children: 'Page Title'
  }
};

// Rendering of the object returned by createElement()
var root = ReactDOM.createRoot(
  document.getElementById('root')
);
root.render(element);

Because our focus today is primarily on the syntax extension of JSX, rather than React itself, we won’t go into detail about the element object returned and the rendering process. However, it is worth noting that you can configure Babel to transpile functions for creating elements other than createElement() in React. This allows you to use similar JSX expressions outside of React.

Expressions in JS and CSS #

Previously, we learned about the core concepts of JSX syntax extension. Now, let’s explore how it can be used in conjunction with JavaScript and CSS expressions.

An important feature of React elements is the ability to embed standard JavaScript expressions within JSX expressions. You can use curly braces to embed standard JavaScript expressions. The script inside the curly braces will be interpreted as standard JavaScript during the transpilation process. Nested expressions within React elements can be used as attribute values and child elements. Here’s an example:

function article(className, title, content, linebreak=true) {
  return (
    <div className={className}>
      <h1>{title}</h1>
      { linebreak && <br /> }
      <p>{content}</p>
    </div>
  );
}

function article(className, title, content, subtitle=true) {
  return React.createElement("div", { className: className },
                             React.createElement("h1", null, title),
                             subtitle && React.createElement("br", null),
                             React.createElement("p", null, content));
}

In this example, the article() function returns a JSX element with four parameters. Babel will transpile the code in the example to the following:

This code is easy to read and understand: After transpilation, the curly braces disappear and the generated code passes the arguments given to the article() function to React.createElement(). Pay attention to the usage of the linebreak parameter and the && operator here. In the actual invocation, if only three arguments are passed to article(), it defaults to true, and the fourth argument of the outer createElement() call is an element. However, if we pass false as the fourth argument to article(), then the value of the fourth argument of the outer createElement() call will be false, and the element will not be created. The usage of the && operator is a common practice in JSX, which conditionally includes or excludes child elements based on the value of other expressions. This practice is suitable for React, as React ignores child elements that are false or null and does not generate any output for them.

When using JavaScript expressions within JSX expressions, you are not limited to basic types like strings and booleans as shown in the previous example, but can also use JavaScript values of object types. In fact, using object, array, and function values is very common in React. For example, in the following function, JavaScript and CSS expressions are used as follows:

function list(items, callback) {
  return (
    <ul style={{padding:10, border:"solid red 4px"}}>
      {items.map((item,index) => {
        <li onClick={() => callback(index)} key={index}>{item}</li>
      })}
    </ul>
  );
}

Here, the function uses an object literal as the value of the CSS style property on the <ul> element. Note that double curly braces are needed here. The <ul> element has one child, but the value of that child is an array. The output array is created by using the map() mapping method on the input array to create <li> child elements. And here, each nested <li> child element has an onClick event handler attribute with a value of an arrow function.

If we compile the above JSX code into standard JavaScript code, it will look like this:

function list(items, callback) {
  return React.createElement(
    "ul",
    { style: { padding: 5, border: "dotted green 3px" } },
    items.map((item, index) =>
      React.createElement(
        "li",
        { onClick: () => callback(index), key: index },
        item
      )
    )
  );
}

React Element Types #

In addition, JSX has a more important feature, which is defining the type of React element.

All JSX elements start with an identifier, immediately following the opening angle bracket. If the first letter of the identifier is lowercase, the identifier is passed as a string to createElement(). However, if the first letter of the identifier is uppercase, it is treated as an actual identifier, and the JavaScript value of that identifier is passed as the first argument to createElement().

This means that the JSX expression <CustomButton/> compiles to JavaScript code that passes the global CustomButton object to React.createElement(). For React, this ability to pass a non-string value as the first argument to createElement() can be used to create components.

The simplest way to define a new component in React is to write a function that takes props object as a parameter and returns a JSX expression. The props object is just a JavaScript object representing the attribute values, similar to the object passed as the second argument to createElement(). For example, here is an alternative way to write our article() function:

function Article(props) {
  return (
    <div>
      <h1>{props.title}</h1>
      { props.linebreak && <br /> }
      <p>{props.content}</p>
    </div>
  );
}

function article(className, title, content, linebreak=true) {
  return (
    <div className={className}>
      <h1>{title}</h1>
      { linebreak && <br /> }
      <p>{content}</p>
    </div>
  );
}

This new function Article() is similar to the previous article() function. But its name starts with an uppercase letter, and it only takes one object as a parameter. This allows it to be a React component, which means it can be used as a replacement for standard HTML tags in JSX expressions:

var article = <Article title="文章标题" content="文章内容"/>;

The <Article/> element transpiles to the following:

var article = React.createElement(Article, {
  title: "文章标题",
  content: "文章内容"
});

This is a simple JSX expression, but when React renders it, it will pass the second argument, which is the props object, to the first argument, which is the Article() function, and will use the JSX function returned by that function instead of the <Article/> expression.

Furthermore, if we have multiple components in one module, we can also use the dot notation to express the components in the module:

import React from 'react';
var MyComponents = {
  Calendar: function Calendar(props) {
    return <div>一个{props.color}颜色的日历.</div>;
  }
}
function GreenCalendar() {
  return <MyComponents.DatePicker color="绿" />;
}

Another use of object expressions in JSX is to use the object spread operator to specify multiple properties at once. If you write a set of JSX expressions with common attributes that may be reused, you can simplify the expressions by abstracting the attributes into an attribute object and “spread” it into the JSX element.

var greeting = <div firstName="三" lastName="张" />;

var props = {firstName: '三', lastName: '张'};
var greeting = <div className = "greeting" {...props} />;

Babel compiles it into a function that uses _extends() to combine the className attribute with the properties contained in the props object.

var greeting = React.createElement("div",
                                 _extends({className: "greeting"}, props)
                                );

Summary #

Today, we’ve seen how to express DOM elements in JavaScript using JSX, a JavaScript extension. There are several benefits to doing this:

  1. It empowers UI-driven and event-driven development by allowing us to write JSX expressions that are transpiled into createElement() function calls using the Babel compiler we discussed earlier. These function calls create and return objects that define the desired elements. Finally, we use the render() method to render the element objects in the browser. Additionally, JSX allows for the intermixing of JavaScript and CSS expressions. Furthermore, besides basic data types, we can also use array and function types.
  2. Utilizing JSX also aids in modular and component-based development.
  3. Lastly, from a security standpoint, JSX automatically converts all content into strings before rendering, effectively preventing cross-site scripting attacks (XSS) we mentioned in the security section. This ensures our application’s safety.

Thought Question #

Finally, I have a thought question for you. Actually, before JSX, there were many template engines based on JavaScript, such as jQuery Template and Mustache.js. Do you think there are any differences between JSX and traditional JavaScript template engines? What are their respective advantages and disadvantages?

Feel free to share your experiences, exchange learning insights, or ask questions in the comments section. If you find value in today’s content, please feel free to share it with more friends. See you in the next class!