跳至主要内容

如何讓 shadcn/ui Select 元件 value 能夠接收空字串及其他型別

· 閱讀時間約 5 分鐘

shadcn/ui 雖然主打隨時都能修改 source code 好像很彈性,但某些元件還是依賴其他第三方的 ui library,例如 Select 元件依賴@radix-ui/react-select,導致沒辦法輕易做到修改更底層的 source code。

而最近使用 shadcn/ui 也遇到蠻多坑的,其中一個讓我覺得最麻煩,一定得想辦法處理的就是 Select 元件 value 無法接收空字串及其他型別的問題,而限制空字串這還是新版@radix-ui/react-select 的 feature...

先說說不能空字串只能字串會分別遇到什麼痛點好了

不能空字串

原本專案有個 filter search 功能,GET 能帶 status 參數,/api/orders?status="success"表示搜尋狀態為成功的訂單,/api/orders?status="error"表示搜尋狀態為失敗的訂單, 而/api/orders 表示搜尋所有狀態的訂單。

這時如果傳入物件有個 value""空字串的 key 給 axios params,我們會在 axios 的 paramsSerializer (參數序列化)或 interceptors (攔截器)過濾該物件的 key,你的 URL 還是/api/orders

但如果今天限制不能只用空字串,你就必須自定義一個常量變數用來表示全部狀態, 例如"__ALL__",然後去改變專案原本的 paramsSerializer (參數序列化)或 interceptors (攔截器),不然你就只能在 submit 的時候必須先判斷是否為"__ALL__",如果是的話就要先移除該 key 或改為空字串才能打 API,相當麻煩,況且如果遇到不是打 API,而是其他業務邏輯就是要空字串的情況怎辦?

只能字串

而只能使用字串會遇到打 POST API 或其他業務邏輯時,如果需要的參數為數字、布林值等,就必須在 submit 或處理之前將該字串手動判斷轉換為需要的型別,非常麻煩外,可讀性差又難以維護,在 react-hook-form 的 formValues 型別及驗證上也全是字串,根本沒有意義。

為了解決這問題,只能自己封裝一個 Select 元件了,而解決的思路其實也很簡單,因為不能動到底層的@radix-ui/react-select,所以我們還是傳字串valueSelectItem,只不過會先根據 value 的型別進行處理:

const valueToString = (val: any) => {
if (val === null) return "__null__"
if (val === undefined) {
console.error("option value cannot be undefined")
return "__undefined__"
}
if (val === Infinity) return "__Infinity__"
if (val === -Infinity) return "__-Infinity__"
if (Number.isNaN(val)) return "__NaN__"
if (typeof val === "symbol") return `__symbol__${val.description}`
return JSON.stringify(val)
}

然後在SelectonValueChange 事件中,將拿到的字串value再轉換回原本的型別給上層的onChange使用,

const stringToValue = (str: string) => {
switch (str) {
case "__null__":
return null
case "__undefined__":
return undefined
case "__Infinity__":
return Infinity
case "__-Infinity__":
return -Infinity
case "__NaN__":
return NaN
default:
if (str.startsWith("__symbol__")) {
return Symbol(str.slice(10))
}
return JSON.parse(str)
}
}
const Select = forwardRef<HTMLButtonElement, SelectProps>(
({ options, placeholder, onChange, value }, ref) => {
const handleValueChange = (value: string) => {
if (onChange) {
onChange(stringToValue(value))
}
}

return (
<SelectUI
// Only show placeholder when value is initially undefined
value={value !== undefined ? valueToString(value) : undefined}
onValueChange={handleValueChange}
>
<SelectTrigger ref={ref} className="w-[180px]">
<SelectValue placeholder={placeholder} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{options.map((option) => (
<SelectItem
key={valueToString(option.value)}
value={valueToString(option.value)}
disabled={option.disabled}
>
{option.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</SelectUI>
)
}
)

這樣就大功告成了,同理Radio Group也是一樣的道理,畢竟很常遇到truefalse要手動字串轉布林值的問題。

範例程式碼:

Demo

GitHub

PowerShell 無法執行 npm

· 閱讀時間約 2 分鐘

執行 npm 時出現以下錯誤:

npm : 因為這個系統上已停用指令碼執行
。如需詳細資訊,請參閱 about_Executi
?LinkID=135170。

問了一下 AI 就解決了,紀錄一下

看起來您遇到了 PowerShell 的執行政策(Execution Policy)限制的問題。這是一個常見的 Windows 安全性設置,預設會阻止執行腳本。 以下是解決方案:

  1. 以系統管理員身份開啟 PowerShell,然後執行以下其中一個指令:
# 選項1:設置當前使用者的執行政策
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned

# 選項2:如果需要更寬鬆的設置(不建議用於生產環境)
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
  1. 當系統詢問是否要更改執行政策時,輸入 "Y" 確認。
  2. 完成後,您就應該能夠正常執行 npm 命令了。 補充說明:
  • RemoteSigned 政策允許執行本地腳本和已簽名的遠端腳本
  • Unrestricted 政策允許執行所有腳本(較不安全)
  • 建議使用 RemoteSigned,這樣可以在保持基本安全性的同時允許開發工作 如果您不想永久更改執行政策,也可以在執行 npm 命令時使用以下方式繞過限制:
powershell -ExecutionPolicy Bypass npm install

請注意,修改執行政策可能需要系統管理員權限。如果您在公司電腦上遇到這個問題,可能需要聯繫 IT 部門獲取相應權限。