正在載入索引…

React:JSX 是什麼?為何在 React 中使用 JSX?

3.6k 字,約 4 分鐘

文章目錄
  1. 什麼是 JSX? 為什麼要用JSX?
  2. JSX內部原理及規則
    1. 使用者定義的元件必須大寫
    2. React元件不能使用表達式(只能是大寫字母開頭的變數或函數名)
    3. 回傳單一個根元素
    4. 所有標籤都需要結尾標籤
  3. JSX特色
    1. Props可以使用Javascript表達式(foo={1 + 2 + 3 + 4})
    2. 純布林值、Null 和未定義值將被忽略(以下案例全部均顯示空白)
    3. 屬性展開(...)
    4. 陣列作為子元素: 你可以使用陣列來傳遞多個子元素
    5. React內聯樣式(inline styles)

什麼是 JSX? 為什麼要用JSX?

  • JSX(JavaScript XML)是一種 JavaScript 的語法擴展,通常與 React 一同使用。它允許開發者在 JavaScript 中使用類似 XML 或 HTML 的語法來撰寫 React 元素,並透過渲染(Render)將其轉譯為實際的 DOM 元素及相對應的事件。這種語法使得 React 組件的描述更直觀、易讀,同時提供了更高效的元素創建方式。
  • JSX只是為React element(原生語法)提供語法糖。
  • 原生創建React元素語法:React.createElement(component, props, ...children)。
    • component:React 元素的類型,可以是 HTML 元素名稱(字符串,例如 'div')或 React 組件
    • props:元素的屬性(attributes),是一個包含元素屬性及其值的物件。
    • ...children:元素的子元素,可以是單一元素或一個包含多個子元素的數組。
javascript
React.createElement(
  div,
  {className: 'info'},
  'Hello World'
)
  • JSX寫法如下
javascript
<div className="info">Hello World</div>

  • 巢狀寫法的案例如下:
    • React.createElement寫法
javascript
React.createElement(
  'div',
  { className: 'container' },
  React.createElement(
    'h1',
    null,
    'Nested Example'
  ),
  React.createElement(
    'p',
    { style: { color: 'blue' } },
    'This is a nested paragraph.'
  )
);
  • JSX寫法
javascript
<div className="container">
  <h1>Nested Example</h1>
  <p style={{ color: 'blue' }}>This is a nested paragraph.</p>
</div>

JSX內部原理及規則

  • JSX會在編譯階段被轉譯成對應的 React.createElement 的呼叫,生成一個 JavaScript 物件,這個物件描述了 React 元素的類型、屬性(props),以及子元素。

JSX使用規則如下

使用者定義的元件必須大寫

  • 錯誤寫法
javascript
import React from 'react';
// ↓ 正確:hello->Hello
function hello(props) {
  return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
  // ↓ 使用hello非大寫會導致發生錯誤,無法透過JSX引用該元件
  //   正確的方式:hello->Hello
  return <hello toWhat="World" />;
}
  • 正確寫法
javascript
import React from 'react';

function Hello(props) {
  return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
  return <Hello toWhat="World" />;
}

React元件不能使用表達式(只能是大寫字母開頭的變數或函數名)

  • 錯誤寫法
javascript
import React from 'react';

// PhotoStory 元件
function PhotoStory(props) {
  return <div>Photo Story: {props.story}</div>;
}
// VideoStory 元件
function VideoStory(props) {
  return <div>Video Story: {props.story}</div>;
}
// Story 元件
const Components = {
  photo: PhotoStory,
  video: VideoStory
};
function Story(props) {
  // ↓ 請用變數賦值,避免使用表達式
  return <Components[props.storyType]; story={props.story} />;
}
// App 元件
function App() {
  return (
    <div>
      <Story storyType="photo" story="A beautiful photo story" />
      <Story storyType="video" story="An amazing video story" />
    </div>
  );
}

export default App;
javascript
import React from 'react';

// PhotoStory 元件
function PhotoStory(props) {
  return <div>Photo Story: {props.story}</div>;
}
// VideoStory 元件
function VideoStory(props) {
  return <div>Video Story: {props.story}</div>;
}
// Story 元件
const components = {
  photo: PhotoStory,
  video: VideoStory
};
function Story(props) {
  // 根據 props 中的 storyType 選擇要渲染的元件
  const SpecificStory = components[props.storyType];
  // 渲染所選擇的元件,並將 story 作為 props 傳遞
  return <SpecificStory story={props.story} />;
}
// App 元件
function App() {
  return (
    <div>
      <Story storyType="photo" story="A beautiful photo story" />
      <Story storyType="video" story="An amazing video story" />
    </div>
  );
}
export default App;

顯示如下:

javascript
Photo Story: A beautiful photo story
Video Story: An amazing video story

回傳單一個根元素

  • 最外層需要使用父層元素包覆(EX:div、或使用 Fragment (<> 和 </>))
  • 案例如下:
javascript
import React from 'react';

function App() {
  return (
    <>
      <div>Content 1</div>
      <div>Content 2</div>
    </>
  );
}

所有標籤都需要結尾標籤

範例如下:

javascript
<div></div>
  • 自閉標籤(self-closing)範例如下
javascript
<img />

JSX特色

Props可以使用Javascript表達式(foo={1 + 2 + 3 + 4})

javascript
function MyComponent(props) {
    return <div>由App組件傳過來的foo參數:{props.foo}</div>
}
function App() {
    return (
        <div>
            <MyComponent foo={1 + 2 + 3 + 4} />
        </div>
    );
}
export default App;

顯示如下:

javascript
由App組件傳過來的foo參數:10
  • if跟for不是表達式不能使用
  • 三元運算符(肚子餓了嗎?餓了:還沒餓)及邏輯運算符(&&)算是表達式的一種
  • 三元運算符案例如下:
  • Codepen連結
javascript
import React, { useState } from 'react';

const Greeting = ({ isLoggedIn }) => {
  return (
    <div>
      {isLoggedIn ? (
        <p>Welcome back!</p>
      ) : (
        <p>Please log in.</p>
      )}
    </div>
  );
};
const App = () => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const handleLoginToggle = () => {
    setIsLoggedIn((prevIsLoggedIn) => !prevIsLoggedIn);
  };
  return (
    <div>
      <button onClick={handleLoginToggle}>
        {isLoggedIn ? 'Logout' : 'Login'}
      </button>
      <Greeting isLoggedIn={isLoggedIn} />
    </div>
  );
};

export default App;
javascript
import React, { useState } from 'react';

const App = () => {
    const isLoggedIn = true
    return (
        <div>
            {isLoggedIn && <p>Welcome back!</p>}
        </div>
    );
};

export default App;

顯示如下:

javascript
Welcome back!

純布林值、Null 和未定義值將被忽略(以下案例全部均顯示空白)

javascript
<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{undefined}</div>

<div>{true}</div>

屬性展開(...)

javascript
const App = () => {
    const props = { className: 'my-class', style: { color: 'red' } };
    return (
        <div>
            <div {...props}>Hello, World!</div>
        </div>
    );
};

export default App;

陣列作為子元素: 你可以使用陣列來傳遞多個子元素

javascript
import React, { useState } from 'react';

const App = () => {
    const items = ['Item 1', 'Item 2', 'Item 3'];
    return (
        <div>
            <ul>
                {items.map((item, index) => (
                    <li key={index}>{item}</li>
                ))}
            </ul>
        </div>
    );
};

export default App;

React內聯樣式(inline styles)

  • JSX不能使用保留字(會自動轉換成別的字詞避免與保留字衝突)
    • class->className
    • for->htmlfor(label)
    • CSS內聯屬性stroke-width改成駝峰式命名(Camel-Case)strokeWidth
javascript
const App = () => {
    return (
        <div>
            <div style={{ color: 'red', fontSize: '16px' }}>Hello, World!</div>
        </div>
    );
};

留言

留言使用 GitHub 帳號登入(Giscus),留言會存在 GitHub Discussions。