跳至主要内容

這幾年我學到的前端處理條件判斷的方法

· 閱讀時間約 4 分鐘
阿昇
Software Engineer

所有會寫程式的人,應該都對條件判斷不陌生,不論什麼領域都常遇到。

這篇文章主要分享我作為前端工程師暨 JS 使用者,在這幾年學會的條件判斷處理方式。

多種情況執行同個動作

最基礎的處理方式就是利用 if 和邏輯或 || 來處理,例如

if (key === "a" || key === "b" || key === "c") {
/* do something */
}

也有人會用 switch 來把那些狀況放在同個 code block

switch (key) {
case "a":
case "b":
case "c":
/* do something */
break;
default:
/* do something */
}

不過我目前比較喜歡的方式是用 Array.prototype.includes() 來處理

if (["a", "b", "c"].includes(key)) {
/* do something */
}

因為 includes 這個 method 會檢查 array 中的每個 element,只有有其中一個 element 符合 key 那就會得到 true,所以我們可以利用這個特性來處理這種狀況。

根據不同狀況賦值

最直白的寫法一樣是用 if 寫出不同的 block

let result;
if (key === "a") {
result = "A";
} else if (key === "b") {
result = "B";
} else if (key === "c") {
result = "C";
} else {
/* do something */
}

再來我們也可利用 switch 來處理,應該是 js 初學者也常學習到的處理方式

let result;
switch (key) {
case "a":
result = "A";
break;
case "b":
result = "B";
break;
case "c":
result = "C";
break;
default:
/* do something */
}

上述方式都可以解決問題,但是寫法就是不俐落,而且狀況一旦變多就難以閱讀。

其實有種簡單的方式適合處理這類狀況,那就是利用 object literals;object 是根據 hashmap 實作的,也就是我們可以用不同的 key 來取得其中不同的 value,於是我們可以利用這個特性來改變上述內容。

const result = {
a: "A",
b: "B",
c: "C",
}[key];

如果沒有符合的條件,但又希望給變數賦值一個預設內容,則可以這樣處理

const obj = {
a: "A",
b: "B",
c: "C",
default: "DEFAULT",
};

const result = obj[key] || obj["default"];

或是這樣

const result =
{
a: "A",
b: "B",
c: "C",
}[key] ?? "DEFAULT";

根據不同狀況渲染不同內容

再來看看前端常遇到的例子,我們時常會根據不同情況渲染不同的 UI,例如 fetch 的東西還沒回來,所以現在應該顯示 loading 狀態,以前我會這樣處理(這邊以 React 為例)

import useAxios from 'axios-hooks'

const App = () => {
const [{ data, loading }, refetch] = useAxios(
"https://example.com/api/users?id=1"
);

return loading ? <p>Loading...</p> ? <p>{data.name}</p>
}

或是這樣

import useAxios from "axios-hooks";

const App = () => {
const [{ data, loading }, refetch] = useAxios(
"https://example.com/api/users?id=1"
);

if (loading) return <p>Loading...</p>;
return <p>{data.name}</p>;
};

這樣寫雖然很直覺,可是也同樣地若內容變多就難以閱讀,也會造成程式碼難以維護,所以後來我們團隊習慣使用 ts-pattern 來幫助我們處理類似問題。

import useAxios from "axios-hooks";
import { match, P } from "ts-pattern";

const App = () => {
const [{ data, loading }, refetch] = useAxios(
"https://example.com/api/users?id=1"
);

return match(loading)
.with(true, () => <p>Loading...</p>)
.with(false, () => <p>{data.name}</p>)
.exhaustive();
};

ts-pattern 這個套件也不只是可用在這個狀況,也可處理一些初學者容易寫成巢狀的條件判斷,例如

import useAxios from "axios-hooks";

const App = () => {
const [{ data, loading }, refetch] = useAxios(
"https://example.com/api/users?id=1"
);

if (loading) {
return <p>Loading...</p>;
} else {
if (!data.length) {
return <p>No data</p>;
} else {
return (
<div>
{data.map((item) => (
<p key={item.id}>{item.name}</p>
))}
</div>
);
}
}
};

上述巢狀情形可以先改寫成

import useAxios from "axios-hooks";

const App = () => {
const [{ data, loading }, refetch] = useAxios(
"https://example.com/api/users?id=1"
);

if (loading) return <p>Loading...</p>;
if (!data.length) return <p>No data</p>;

return (
<div>
{data.map((item) => (
<p key={item.id}>{item.name}</p>
))}
</div>
);
};

或是可以利用 ts-pattern 將它改寫成

import useAxios from "axios-hooks";
import { match, P } from "ts-pattern";

const App = () => {
const [{ data, loading }, refetch] = useAxios(
"https://example.com/api/users?id=1"
);

return match({ loading, hasData: data.length })
.with({ loading: true }, () => <p>Loading...</p>)
.with({ hasData: false }, () => <p>No data</p>)
.otherwise(() => (
<div>
{data.map((item) => (
<p key={item.id}>{item.name}</p>
))}
</div>
));
};

總結

這些方法都是我在工作時從同事學來的,看自己現在與一兩年前的寫法在可讀性上進步許多,非常有感而想寫出來分享。

希望這些內容對讀者能有所幫助,如果有任何想討論的,都歡迎留言。

參考資料如下,分享給有興趣閱讀的朋友看看: