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

Blog背景圖

什麼是 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:元素的子元素,可以是單一元素或一個包含多個子元素的數組。
1
2
3
4
5
React.createElement(
div,
{className: 'info'},
'Hello World'
)
  • JSX寫法如下
1
<div className="info">Hello World</div>

  • 巢狀寫法的案例如下:
    • React.createElement寫法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
React.createElement(
'div',
{ className: 'container' },
React.createElement(
'h1',
null,
'Nested Example'
),
React.createElement(
'p',
{ style: { color: 'blue' } },
'This is a nested paragraph.'
)
);
  • JSX寫法
1
2
3
4
<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使用規則如下

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

  • 錯誤寫法
1
2
3
4
5
6
7
8
9
10
11
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" />;
}
  • 正確寫法
1
2
3
4
5
6
7
8
9
import React from 'react';

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

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

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

  • 錯誤寫法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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;

顯示如下:

1
2
Photo Story: A beautiful photo story
Video Story: An amazing video story

回傳單一個根元素

  • 最外層需要使用父層元素包覆(EX:div、或使用 Fragment (<> 和 </>))
  • 案例如下:
1
2
3
4
5
6
7
8
9
10
import React from 'react';

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

所有標籤都需要結尾標籤

範例如下:

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

JSX特色

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

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

顯示如下:

1
App組件傳過來的foo參數:10
  • if跟for不是表達式不能使用
  • 三元運算符(肚子餓了嗎?餓了:還沒餓)及邏輯運算符(&&)算是表達式的一種
  • 三元運算符案例如下:
  • Codepen連結
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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;
1
2
3
4
5
6
7
8
9
10
11
12
import React, { useState } from 'react';

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

export default App;

顯示如下:

1
Welcome back!

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

1
2
3
4
5
6
7
8
9
10
11
<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{undefined}</div>

<div>{true}</div>

屬性展開(…)

1
2
3
4
5
6
7
8
9
10
const App = () => {
const props = { className: 'my-class', style: { color: 'red' } };
return (
<div>
<div {...props}>Hello, World!</div>
</div>
);
};

export default App;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
1
2
3
4
5
6
7
const App = () => {
return (
<div>
<div style={{ color: 'red', fontSize: '16px' }}>Hello, World!</div>
</div>
);
};