有時候你的 component 會需要根據不同的條件來顯示不同的內容。在 React 中,你可以使用 JavaScript 中 if
陳述式、&&
以及 ? :
運算子等語法來根據條件 render 不同的 JSX。
You will learn
- 如何根據不同條件回傳不同 JSX
- 如何有條件地包含或排除一段 JSX
- React codebases 中常見的條件式簡短語法
條件性地回傳 JSX
舉例來說,你在 PackingList
component 中 render 了數個可以被標記為打包完成與否的 Item
component:
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> ); }
注意有些 Item
component 的 isPacked
prop 的值是 true
而非 false
。而你想要在 isPacked={true}
的情況下,為已打包的項目加上一個勾號 (✔)。
你可以將此情境用以下 if
/else
陳述式來表示:
if (isPacked) {
return <li className="item">{name} ✔</li>;
}
return <li className="item">{name}</li>;
如果 isPacked
prop 的值為 true
,這段程式碼會回傳一個不同的 JSX tree。藉由此更動,部分項目內容的最後會有一個勾號。
function Item({ name, isPacked }) { if (isPacked) { return <li className="item">{name} ✔</li>; } 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> ); }
請嘗試更改在不同情況下回傳的內容,並觀察結果會有何不同!
注意你是如何使用 JavaScript 的 if
和 return
陳述式建立分支邏輯的。在 React 中,控制流程 (例如條件) 是交由 JavaScript 來處理的。
條件式地使用 null
代表不回傳任何內容
在某些情況下,你可能不想 render 任何東西。舉例來說,你不想顯示任何已經打包好的項目,但 component 必須要有個回傳值。在這種情況下,你可以回傳 null
:
if (isPacked) {
return null;
}
return <li className="item">{name}</li>;
如果 isPacked
的值為 true
,component 不回傳任何東西 (意即 null
)。否則,component 會回傳要 render 的 JSX。
function Item({ name, isPacked }) { if (isPacked) { return null; } 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> ); }
在實際開發中,component 回傳結果為 null
可能並不在 render 該 component 的開發者的預期之內,所以此方法並不常使用。較為常見的方法是在 parent component 的 JSX 中,條件性地決定是包含或移除此 component。實作方法如下!
條件性地包含 JSX
在先前的範例中,你可以控制哪個 JSX tree 會被 component 回傳 (如果有的話!)。而你可能已經注意到 render 內容中有一些重複的部分:
<li className="item">{name} ✔</li>
與下方段落非常相似
<li className="item">{name}</li>
兩個條件分支都會回傳 <li className="item">...</li>
:
if (isPacked) {
return <li className="item">{name} ✔</li>;
}
return <li className="item">{name}</li>;
雖然這個重複不會造成太大影響,但它可能會讓你的程式碼維護起來較不容易。假如你想要修改 className
的值呢?你會需要修改兩處程式碼!在這種情況下,你可以有條件地包含一些 JSX,讓你的程式碼更符合 DRY 的原則。
條件 (三元) 運算子 (? :
)
JavaScript 有一個簡潔的語法可以用來撰寫條件表達式—條件運算子 或「三元運算子」。
不使用這種寫法:
if (isPacked) {
return <li className="item">{name} ✔</li>;
}
return <li className="item">{name}</li>;
你可以改為以下寫法:
return (
<li className="item">
{isPacked ? name + ' ✔' : name}
</li>
);
你可以把這段程式碼解讀為:「如果 isPacked
的值為 true
,則 (?
) render name + ' ✔'
;否則 (:
),render name
」。
Deep Dive
如果你有物件導向開發的相關背景,你或許會因為其中一個範例有可能會建立兩個不同的 <li>
instance,從而認為上面的兩個範例略有不同。但 JSX 元素其實並不是 instance,因為它們沒有保存任何內部狀態,也不是真正的 DOM 節點。它們是像藍圖一樣的輕量化描述。所以上述兩個例子事實上 是 全等價的。保留與重設狀態 詳細介紹了它的運作原理。
現在假設你想將打包完成項目的文字用另一個 HTML 標籤包起來,像是藉由 <del>
為文字加上刪除線。你可以藉由增加換行和括弧,以方便為兩種情況加入更多 JSX:
function Item({ name, isPacked }) { return ( <li className="item"> {isPacked ? ( <del> {name + ' ✔'} </del> ) : ( 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> ); }
這種寫法適用於條件式簡單的情況,但要適度的使用。如果你的 component 因包含太多巢狀的條件標記而變得雜亂,應該要考慮提取出 child component 以進行程式碼整理。在 React 中,標記是你程式碼的一部分,所以你可以使用像是變數和函式這樣的工具來整理複雜的表達式。
邏輯 AND 運算子 (&&
)
另一個常見的簡短語法是 JavaScript 邏輯 AND (&&
) 運算子。在 React component 中,時常會遇到在條件成立時 render 一些 JSX,否則不呈現任何東西的情境。藉由使用 &&
,你可以只有在 isPacked
的值為 true
時呈現勾號:
return (
<li className="item">
{name} {isPacked && '✔'}
</li>
);
你可以把這段程式碼解讀為:「如果 isPacked
為真,就(?
)render 勾號;否則(:
),什麼都不 render 」。
實際用法如下:
function Item({ name, isPacked }) { return ( <li className="item"> {name} {isPacked && '✔'} </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> ); }
JavaScript && 運算子 會在左側的條件為 true
時回傳它右側的值(在範例中是勾號)。但如果條件為 false
,整個表達式的結果就會變成 false
。跟 null
或 undefined
一樣,React 會將 false
視為像是 JSX tree 中的一個 「洞」,因此不會 render 任何東西。
有條件地將 JSX 指定給變數
當使用簡短語法造成在寫程式碼本身有所不便時,你可以嘗試使用 if
陳述式和一個變數。使用 let
宣告的變數的值是允許你修改的, 所以可以先將你預設希望顯示的內容指定給這個變數,也就是 name:
let itemContent = name;
接著在 isPacked
的值為 true
時,使用 if
陳述式重新將 itemContent
的值指定為一個 JSX 表達式。
if (isPacked) {
itemContent = name + " ✔";
}
大括弧打開了引入 JavaScript 的大門。 由大括弧可以將變數嵌入回傳的 JSX tree 中,進而將先前計算的表達式巢狀地嵌在 JSX 中:
<li className="item">
{itemContent}
</li>
這種寫法是最冗長的,卻也是最靈活的。實際用法如下:
function Item({ name, isPacked }) { let itemContent = name; if (isPacked) { itemContent = name + " ✔"; } return ( <li className="item"> {itemContent} </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> ); }
與前述相同,這寫法不只適用於文字,也同樣適用於任意的 JSX :
function Item({ name, isPacked }) { let itemContent = name; if (isPacked) { itemContent = ( <del> {name + " ✔"} </del> ); } return ( <li className="item"> {itemContent} </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> ); }
如果你不熟悉 JavaScript,一開始可能會對這些不同的寫法感到不知所措。但學會它們有助於你閱讀和撰寫任何 JavaScript 程式碼——而不僅僅是 React component!挑一個你偏好的寫法作為起始,如果忘記了其他的寫法再回來參考即可。
Recap
- 在 React 中,你透過 JavaScript 控制邏輯的分支。
- 你可以使用
if
表達式有條件地回傳 JSX 表達式。 - 你可以有條件地將一些 JSX 指定到一個變數,然後使用大括弧將它包起來以巢狀地嵌入其他 JSX 中。
- 在 JSX 中,
{cond ? <A /> : <B />}
表示 「如果cond
為真,則 render<A />
,否則 render<B />
」。 - 在 JSX 中,
{cond && <A />}
表示 「如果cond
為真,則 render<A />
,否則什麼都不 render」。 - 簡短寫法很常見,但如果你偏好單純的
if
陳述式,完全可以不使用這些簡短寫法。
Challenge 1 of 3: 使用 ? :
為未打包完成的項目加上圖示
使用條件運算子(cond ? a : b
)在 isPacked
的值不是 true
時 render 一個 ❌。
function Item({ name, isPacked }) { return ( <li className="item"> {name} {isPacked && '✔'} </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> ); }