終究都要學 React 何不現在學呢? - React 進階 - Fragments - (18)

前言

Fragments 是一個實戰上非常常使用的技巧,它可以讓我們在寫 React 的時候,可以不用一定要包一層 div,這樣就可以讓我們的 HTML 結構更加乾淨,那麼該怎麼使用呢?讓我們一起來看看吧!

為什麼要使用 Fragments

在說明為什麼要使用 Fragments 之前,我們先來看看一個簡單的範例:

1
2
3
4
5
6
7
8
9
10
11
const App = () => {
return (
<div>
<h1>Fragments Sample</h1>
<h1>Fragments Sample</h1>
</div>
)
}

const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App />);

上面是一個很簡單的範例,我們以往開發都會用一個 <div> 包著內層,因為如果你外層不額外包一層的話,就會發生錯誤,什麼錯誤呢?讓我們來看看

1
2
3
4
5
6
7
8
9
const App = () => {
return (
<h1>Fragments Sample</h1>
<h1>Fragments Sample</h1>
)
}

const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App />);

基本上你是可以看到 CodePen 直接跳出一個提示訊息

Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>…</>?

那麼為了解決這個問題,我們可能就會如同一開始範例那樣子使用一個 <div> 包著內層,但是這樣子的話,我們的 HTML 結構就會莫名多一個沒意義的 <div>,當專案越來越龐大時,這個沒有意義的 <div> 就會讓我們的 HTML 結構變得非常雜亂,因此 React 就提供了一個神兵利器 Fragments,讓我們可以不用一定要包一層 <div>,這樣就可以讓我們的 HTML 結構更加乾淨

1
2
3
4
5
6
7
8
9
10
11
const App = () => {
return (
<React.Fragment>
<h1>Fragments Sample</h1>
<h1>Fragments Sample</h1>
</React.Fragment>
)
}

const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App />);

當你使用 React.Fragment 之後你也可以發現原本的錯誤訊息就不見了,而且你的 HTML 結構也變得乾淨許多,這邊我們也可以來觀看 HTML 的結構是不是真的少了莫名其妙的 <div>

React.Fragment

not use React Fragment

相信兩者比較下來,你應該會比較喜歡 React.Fragment

那麼 React.Fragment 其實還有簡寫的方式,就是使用 <>,這樣子你寫起來可以更愉快

1
2
3
4
5
6
7
8
9
10
11
const App = () => {
return (
<>
<h1>Fragments Sample</h1>
<h1>Fragments Sample</h1>
</>
)
}

const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App />);

那麼有沒有發現 React.Fragment 與 Vue 的 <template> 有點像呢?Vue 的 <template> 也是用來包裝多個元素,但是不會在 HTML 結構中產生多餘的元素,讓我們回顧一下 Vue 的 <template> 是如何使用的

1
2
3
4
5
6
<div id="app">
<template v-if="true">
<h1>Template Sample</h1>
<h1>Template Sample</h1>
</template>
</div>
1
2
3
4
5
6
7
8
const { createApp } = Vue;

const app = createApp({
setup() {
}
});

app.mount('#app');

我們可以看到 Vue 的 <template> 也是不會在 HTML 結構中產生多餘的元素

Vue template

前面有提到 <></>React.Fragment 的簡寫,而它也有自己的名稱也就是 Empty Tag,這個名稱是因為它是一個空標籤。

Empty 與 Fragment 的差異

<></>React.Fragment 有什麼差異呢?一般狀況下兩者是沒有什麼太大差異的

1
2
3
4
5
6
7
8
9
10
11
const App = () => {
return (
<>
<h1>Empty Sample</h1>
<h1>Empty Sample</h1>
</>
)
}

const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App />);
1
2
3
4
5
6
7
8
9
10
11
const App = () => {
return (
<React.Fragment>
<h1>Empty Sample</h1>
<h1>Empty Sample</h1>
</React.Fragment>
)
}

const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App />);

但我們通常在跑一段迴圈時,通常 React 會提示你要補上 key,而這時候你就不可以使用 Empty tag,因為 Empty tag 是沒有 keyattribute (屬性) 的,所以你必須使用 React.Fragment 來包著,並將 key 寫在 React.Fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const App = () => {
const list = [1, 2, 3, 4, 5];
return (
<>
{list.map((item, index) => (
<React.Fragment key={index}>
<h1 key={index}>{item}</h1>
</React.Fragment>
))}
</>
)
}


const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App />);

所以這邊我才沒有特別說明 Empty tag 是 React.Fragment 的語法糖,因為它們在使用上有一些差異。

後記

本文將會同步更新到我的部落格