筆記 - React Intro (2) Conditional Rendering

本篇從 React 官網 React Docs BETA的 Conditional Rendering 部分開始。

Conditional Rendering

  • How to return different JSX depending on a condition
  • How to conditionally include or exclude a piece of JSX
  • Common conditional syntax shortcuts you’ll encounter in React codebases

範例 code:

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
function Item({ name, isPacked }) {
return <li className="item">{name}</li>;
}

export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
isPacked={true}
name="Space suit"
/>
<Item
isPacked={true}
name="Helmet with a golden leaf"
/>
<Item
isPacked={false}
name="Photo of Tam"
/>
</ul>
</section>
);
}

Conditionally returning JSX

可以在 component 使用 if/else statement 來 return 不同內容。例如:

1
2
3
4
5
6
function Item({ name, isPacked }) {
if (isPacked) {
return <li className="item">{name} ✔</li>;
}
return <li className="item">{name}</li>;
}
  • Conditionally return nothing with null

假設 if true 就不渲染內容,但實務上不常如此使用。

1
2
3
4
5
6
function Item({ name, isPacked }) {
if (isPacked) {
return null;
}
return <li className="item">{name}</li>;
}

Conditionally including JSX

上方 if/else statement 會造成語法重複,實務上常用以下方式來寫:

Conditional (ternary) operator (? :)

把 if/else statement 改用 三元運算子 來寫,較為簡潔(如果條件單純的話):

1
2
3
4
5
6
7
function Item({ name, isPacked }) {
return (
<li className="item">
{isPacked ? name + ' ✔' : name}
</li>
);
}

唸作: “if isPacked is true, then (?) render name + ' ✔', otherwise (:) render name

可以用小括號包多行 JSX,但太複雜的條件或太多 nested 還是不要這樣寫,易讀性差。

1
2
3
4
5
6
7
8
9
10
11
12
13
function Item({ name, isPacked }) {
return (
<li className="item">
{isPacked ? (
<del>
{name}
</del>
) : (
name
)}
</li>
);
}

Logical AND operator (&&)

若是需要 if true, render some JSX, or nothing otherwise 的時候使用

1
2
3
4
5
6
7
function Item({ name, isPacked }) {
return (
<li className="item">
{name} {isPacked && '✔'}
</li>
);
}

唸作: if isPacked, then (&&) render the checkmark, otherwise, render nothing”

如果 && 左側為 true,會 return 右邊的值(‘✔’)。如果左側為 false,則整個 {isPacked && '✔'} 為 false。 React 會將 false 是為 JSX tree 中的一個洞 (hole),與 nullundefined 一樣,不會在其位置 render 任何內容。

注意:不要在 && 左邊放數字
如果左邊是 0,整個運算式會得到 value 0,React 就會渲染 0 出來。

1
2
let v = 0 && true
console.log(v) // 0

若要呈現 0 的話不渲染,則左邊要寫成判斷式,例如 count > 0 && <p>New Messages</p>

Conditionally assigning JSX to a variable

如果不想用上面那些 operator,就直接用 if statement。

下面例子使用 let 宣告 itemContent 變數,值為 default 顯示的內容。若 if true ,則 reassign JSX expression to itemContent

條件式結果存放到變數中,再放進回傳的 JSX 中,雖然 code 變得比較長,但提高可維護性。

1
2
3
4
5
6
7
8
9
10
11
function Item({ name, isPacked }) {
let itemContent = name;
if (isPacked) {
itemContent = name + " ✔";
}
return (
<li className="item">
{itemContent}
</li>
);
}

透過 object 資料結構來讓 drinks 變得容易維護。

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
32
33
34
35
36
37
38
const drinks = {
tea: {
part: 'leaf',
caffeine: '15–70 mg/cup',
age: '4,000+ years'
},
coffee: {
part: 'bean',
caffeine: '80–185 mg/cup',
age: '1,000+ years'
}
};

function Drink({ name }) {
const info = drinks[name];
return (
<section>
<h1>{name}</h1>
<dl>
<dt>Part of plant</dt>
<dd>{info.part}</dd>
<dt>Caffeine content</dt>
<dd>{info.caffeine}</dd>
<dt>Age</dt>
<dd>{info.age}</dd>
</dl>
</section>
);
}

export default function DrinkList() {
return (
<div>
<Drink name="tea" />
<Drink name="coffee" />
</div>
);
}

Recap of Conditional Rendering

  • In React, you control branching logic with JavaScript.
  • You can return a JSX expression conditionally with an if statement.
  • You can conditionally save some JSX to a variable and then include it inside other JSX by using the curly braces.
  • In JSX, {cond ? <A /> : <B />} means “if cond, render <A />, otherwise <B />”.
  • In JSX, {cond && <A />} means “if cond, render <A />, otherwise nothing”.
  • The shortcuts are common, but you don’t have to use them if you prefer plain if.

Rendering Lists

  • How to render components from an array using JavaScript’s map()
  • How to render only specific components using JavaScript’s filter()
  • When and why to use React keys

Rendering data from arrays

資料以 JavaScript object 與 array 的方式儲存,使用 map()filter() 來取的所需的資料

1
2
3
4
5
6
7
8
const people = [ 'a', 'b', 'c'];

export default function List() {
const listItems = people.map(person =>
<li>{people}</li>
);
return <ul>{listItems}</ul>;
}

Filtering arrays of items

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const people = [{
id: 0,
name: 'a',
gender: 'male',
}, {
id: 1,
name: 'b',
gender: 'female',
}, {
id: 2,
name: 'c',
gender: 'male'
}]

export default function List() {
const male = people.filter(person =>
person.gender === 'male');

const listItems = male.map(person =>
<li>{name}</li>);

return <ul>listItems</ul>/;
}

注意此處寫法
arrow function 隱含了 return => 後面的東西

1
2
const listItems = male.map(person => 
<li>{name}</li>); // Implicit return

如果 arrow function 後面有用 {} 則要寫出 return

1
2
3
const listItems = male.map(person => {
return <li>{name}</li>
});

Keeping list items in order with key

因為 array 中的元素可能在操作過程中有排序、插入、刪除等等的位置改變(index 改變),所以 React 需要 key 來知道每個 item 對應哪個 component,對 DOM tree 正確更新,key 作為 list item 整個生命週期的唯一辨識。

  1. key 不可在渲染的時候同時生成,需要傳進去 component 裡
  2. 如果一個 array item 對應多個 DOM 節點的話,使用完整的 Fragment syntax 來將 key 傳進去(<Fragment></Fragment>),DOM tree 不會渲染 fragment
  3. 如果 component 也需要傳入 id 值,需要用另一個 prop 傳遞 (ex.<Profile key={id} userId={id} />)
1
2
3
4
5
6
const listItems = male.map(person => 
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.gender}</p>
</Fragment>
);

Where to get your key
key 來源:

  1. 資料庫的 id
  2. 本地生成:在產生資料的時候使用 incrementing counter, crypto.randomUUID(), 或是套件例如 uuid

Rules of keys

  1. 同一層的 siblings items 的 key 不可重複。但不同 array 中的 JSX 節點可以使用相同的 key。
  2. 不要在渲染的時候同時產生 key (ex. key={Math.random()})

Recap of Rendering Lists

  • How to move data out of components and into data structures like arrays and objects.
  • How to generate sets of similar components with JavaScript’s map().
  • How to create arrays of filtered items with JavaScript’s filter().
  • Why and how to set key on each component in a collection so React can keep track of each of them even if their position or data changes.