React JSX

React 提供了稱作 JSX 的語法,作為 JavaScript extension syntax,JSX 是一種跟 HTML/XML 很相似的語法,用來寫所謂的 React elements (React 元素)。

JSX 的語法,舉例像是這個簡單的 JavaScript 變數宣告:

const myReactEle = <h1>Hello, world!</h1>;

<h1>Hello, world!</h1> 這一段 code 就是所謂的 JSX,注意它不是字串喔 (前後沒有引號包著),而 myReactEle 這個變數的值則是一個 React element。

JSX 是 React 的樣板語言 (template language),你可以把 JSX 想像成是 JavaScript 的 HTML DOM literal 表示法,這樣你就會更好理解 JSX 了!

React 用了很多 ES6 的語法,如果你還不熟悉的話,可以到這邊學習 ES6

傳統來說,我們常會將 JavaScript 中會用到的 HTML template 散佈在各處,可能是放在 <script> tag 裡面,或放在幾個 template 文件檔案中,然後再用 JavaScript 將這些 template string 取出來 parse 使用,嚴格的執行邏輯 (JavaScript)、樣式 (CSS)、和內容 (HTML) 分離 (separation)。

但 React 的想法則是相反,React 以元件 (components) 為中心思考,認為一個元件的顯示邏輯 (HTML) 和 UI 運作邏輯 (JavaScript) 應該是緊密相關,應該綁在一起的才對,所以有了 JSX。

JSX 第一眼看你可能會覺得怪,但當你開始習慣 React 後,你會覺得 HTML 和 JavaScript 放在一起其實沒什麼問題,反而有好處,讓你一眼看你的 component code 就能夠有完整的資訊,可以知道這個 component 的長相、結構和邏輯是怎樣子的,code 也好維護。

JSX 的背後其實就只是 JavaScript 而已,因為 JSX 不是瀏覽器認識的語法,所以我們還會需要透過 webpackBabel 等工具來將 JSX 轉成單純的 JavaScript 程式碼 - 轉成 React library 的 React.createElement() function calls。

例如以這個例子 JSX:

const element = <h1>Hello, world!</h1>;

透過 Babel 最後會轉成像這樣子的 JavaScript code:

var element = React.createElement(
  "h1",
  null,
  "Hello, world!"
);

看到這邊有沒有更感受到 JSX 帶來的好處?除了可以很直觀的用類 HTML 的語法來描述介面,同時你也不用寫一堆雜亂又冗長的 React.createElement( React.createElement( React.createElement( ... ) ) ) code。

JSX 語法 (Syntax)

先簡單說,JSX 就是 HTML/XML + JavaScript。

除了 HTML,你可以在 JSX 中使用任何 JavaScript expression - 例如 2 + 2, user.firstName, formatName(user) - 在兩個大括號 { } 之間。

例如:

const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);

其中 {formatName(user)} 的地方就會被取代成 formatName function 的返回值。

JSX 最外面的小括號 ( ) 不是必要的,只是個好習慣,避免意外錯誤發生

JSX 可以是巢狀的元素 (nested elements):

const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

留意一件事,在 React 16 以前 (React 15),你需要把 children elements 包在一個 container element 下面喔,像上例中最外層的 <div></div>

而從 React 16 起,新增了 Fragment 的功能,讓你不需要在 DOM 中增加這個額外節點了:

const element = (
  <>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </>
);

就像 XML,如果遇到一個 empty tag,你需要主動 close />

// 正確
const element = <img src={user.avatarUrl} />;

// 錯誤
const element = <img src={user.avatarUrl}>;

標籤名稱大小寫的差別 (Tags Naming Convention)

用 JSX 寫 React elements DOM 結構時,如果是一般的 HTML tags 則用小寫 (lowercase names)。

例如:

const element = <div />;

但如果是 React Component,則 tag 名稱首字用大寫 (capital letter)。

例如:

const element = <Welcome name="Sara" />;

JSX 是 JavaScript expression

JSX 本質是一個 JavaScript expression,所以你可以用在 if 條件、for 迴圈、指定給變數、當作函數參數或返回值等。

例如:

function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}

在 JSX 中使用 if 條件式

因為 { } 中只能放 JavaScript expression,所以在 JSX 你是無法使用 if (if 是一個 JavaScript statement) 的,但是你可以用 JavaScript 三元運算子

例如:

class MyComponent extends React.Component {
  render() {
    return <p>Hello {this.props.someVar ?  'World' : 'Mike'}</p>;
  }
}

JSX 的屬性 (attributes)

跟 HTML 一樣 JSX 的 tag 可以有屬性:

const element = <div tabIndex="0"></div>;

在 React 15,如果是自定義的屬性 (custom DOM attribute),你需要使用 data-*

const element = <div data-myattribute="somevalue" />;

而從 React 16 開始允許你可以用任意的自定義屬性:

const element = <div myattribute="somevalue" />;

當使用 " " 包住的屬性值,它的 type 是字串型態 (string)。不過你可以用 { } 來給不同的 type 或動態變數的屬性值:

const element = <img src={user.avatarUrl} />;

在 JSX 中,如果屬性沒設定值,預設值為 true

const element = <input type="button" disabled />;

// 意思等同於
const element = <input type="button" disabled={true} />;

而如果沒設定某個屬性,預設值則為 false

const element = <input type="button" />;

// 意思等同於
const element = <input type="button" disabled={false} />;

class / className ; for / htmlFor

因為 JSX 底層其實就只是 JavaScript,同時 React 的 API 遵循著 JavaScript DOM API,所以屬性名稱也是按照 JavaScript 原生的語法。

依此原則,舉例如 HTML 中常用到的 class attribute 在 JSX 中則要使用 className;而 for 則是要用 htmlFor。

例如:

const element = <h1 className="title">Hello, world!</h1>;

Spread Attributes

你可以用 ES6 的 Spread Operator 來方便設定屬性。

例如:

var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;

此外,同一個 tag 後面的屬性值會蓋掉前面相同名稱的屬性值,而這特性可以用來做 default values:

var props = { foo: 'default' };
var component = <Component {...props} foo={'override'} />;

// 會顯示 override
console.log(component.props.foo);

Inline Styles (CSS 樣式)

JSX 的 inline style 樣式語法跟你用 JavaScript DOM API 是一樣的 (camelCased properties)。

例子:

const divStyle = {
  color: 'blue',
  backgroundImage: 'url(' + imgUrl + ')',
};

function HelloWorldComponent() {
  return <div style={divStyle}>Hello World!</div>;
}

但 JSX 不會自動幫你處理跨瀏覽器相容問題喔,你需要自己處理:

const divStyle = {
  WebkitTransition: 'all',
  msTransition: 'all'
};

function ComponentWithTransition() {
  return <div style={divStyle}>This should work cross-browser</div>;
}

如果值是數字,JSX 會幫你加上 "px" 單位,若你不想用 px 你就得明確指定單位:

// 值同 10px
<div style={{ height: 10 }}>
  Hello World!
</div>

// 明確指定單位 %
<div style={{ height: '10%' }}>
  Hello World!
</div>

注意有兩個括號 {{ }},第一個 {} 是 JSX 語法的 {},而第二個 {} 表示屬性值是一個 JavaScript object。

JSX 註解 (Comments)

你可以用 /* *///{ } 中作為註解。

各種註解寫法的例子:

const element = (
    <p>
       {/* 單行註解 */}

       {// 多行註解
        // 最後的 } 不能和 // 在同一行
        // ...
       }

       {/* 多行註解
         ....
         ...
         */
       }

       {/* 多行註解
         最後的 } 可以和 */ 在同一行
         ...
         */}
    </p>
);

dangerouslySetInnerHTML

基於安全的考量,React 對於插入 {} 中的字串都會自動幫你做 HTML escape 來避免 XSS (cross-site-scripting) 安全攻擊。

例如下方的例子對於 XSS 攻擊是安全的,因為 React 會幫 title 的值做 HTML escape:

const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;

如果你想要像使用 innerHTML 一樣可以直接塞入任意 HTML,你可以用 React 提供的 dangerouslySetInnerHTML 這個屬性,它接受一個帶有 __html key 的物件。

像是這樣子使用:

function createMarkup() {
  return {__html: 'First &middot; Second'};
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}