如何在 React Native 应用中使用 shadcn/ui [完整手册]

如何在 React Native 应用中使用 shadcn/ui [完整手册]

💡 原文英文,约10300词,阅读约需38分钟。
📝

内容提要

文章介绍如何使用 React Native Reusables、Expo 和 Uniwind 构建咖啡店购物车与结账界面。核心是 shadcn 式注册表机制:CLI 将组件源码复制进项目,开发者完全掌控代码,无需通过新增 props 定制。设计令牌(如 primary、muted)取代 colors.ts,统一管理颜色与明暗主题。文中还讲解移动端要点:用 PortalHost 渲染下拉菜单、安全区适配、键盘处理、点击热区、用 tabular-nums 对齐数字,以及主题需同步更新 global.css 和 lib/theme.ts。

🔎

延伸解读

组件代码归你所有,但更新需手动处理

shadcn 式注册表把组件源码复制进项目,你可以在 Git 中看到并直接修改,无需为每个样式新增 prop。代价是上游修复不会自动同步:若想获取更新,需用 CLI 的 --overwrite 重新拉取,而自定义过的组件可能还要重新应用改动。因此,在大量定制前应权衡维护成本。

设计令牌统一颜色,但需维护两份调色板

文章用 global.css 中的 --color-primary 等令牌取代 colors.ts,组件通过 bg-primary 等类名读取,改一处即可全局生效。不过 React Navigation 读取的是 lib/theme.ts 中的 JavaScript 对象,无法读 CSS 变量,所以同一套颜色要维护两份。若两者不同步,导航栏和页面主题可能不一致。

移动端细节容易静默失败

下拉菜单依赖 PortalHost 渲染,缺失时内容不会显示也不报错;Select 的 onValueChange 返回 Option 对象,但 Web 端 label 只是 value 的副本,若存整个对象会在浏览器显示错误标签。此外,truncate 在 React Native 中不生效,应改用 numberOfLines;小按钮需 hitSlop 扩大点击区域。

主题切换需同时更新 CSS 与导航主题

Uniwind 用 @variant light 和 @variant dark 管理明暗值,调用 Uniwind.setTheme 切换。但导航头部、状态栏和保存的选择需要额外处理:导航主题来自 lib/theme.ts,状态栏样式要随主题调整。文章提醒,只改 global.css 不够,主题需同步更新 global.css 和 lib/theme.ts 两处。

❓

Q&A

shadcn/ui 和传统组件库在 React Native 中的使用方式有什么不同?

shadcn/ui 不是通过 npm 安装组件包,而是通过 CLI 将组件源代码复制到你的项目中,代码完全由你掌控。你可以直接修改组件文件,无需通过新增 props 来定制。React Native Reusables 是其在 React Native 中的实现,使用相同的注册表机制。

React Native Reusables 中的设计令牌(design tokens)如何替代 colors.ts 文件?

设计令牌使用命名颜色角色(如 primary、muted、border)在 global.css 中定义,并通过 Tailwind 类名(如 bg-primary)使用。所有组件从同一处读取颜色,无需维护单独的 colors.ts 文件。同时,lib/theme.ts 中保留一份相同的调色板供 React Navigation 使用,需保持同步。

在 React Native 中使用 Select 下拉菜单时,为什么需要 PortalHost?

下拉菜单内容需要渲染到屏幕其他位置之上,React Native 没有 document.body,因此使用 portal 机制。PortalHost 组件负责读取并显示 portal 内容。如果未挂载 PortalHost,下拉菜单将不会显示且无错误提示。模板已在根布局中包含 PortalHost,必须保留。

React Native Reusables 的 Select 组件在 Web 和原生平台上返回值有什么差异?如何避免相关 bug?

onValueChange 返回一个 Option 对象(包含 value 和 label)。在原生平台上 label 正确,但在 Web 上 label 等于 value。为避免显示错误,应只将 value 存入状态,并从自己的数据列表(如 PICKUP_TIMES)中查找对应的 label。

在 React Native 中如何让 Switch 的标签文字也能切换开关状态?

给 Label 组件添加 onPress 处理函数来切换开关状态,并让 Label 和 Switch 共享相同的 nativeID 以关联无障碍功能。因为 React Native 没有自动的 <label for> 行为,必须手动实现。

在 React Native 中处理键盘遮挡输入框有哪些关键属性?

在 ScrollView 上设置 keyboardShouldPersistTaps="handled" 确保键盘打开时点击仍有效,automaticallyAdjustKeyboardInsets 让 iOS 自动滚动避免输入框被遮挡。对于 Input,设置 autoCapitalize、autoComplete、returnKeyType 等属性优化输入体验。

如何切换 React Native 应用的明暗主题?需要修改哪些文件?

使用 Uniwind.setTheme 切换主题,它会交换 global.css 中 @variant light 和 @variant dark 下定义的颜色值。同时需要更新 lib/theme.ts 中的调色板以保持同步,并调整导航栏和状态栏样式。主题选择可保存到持久存储。

🏷️

标签

➡️

继续阅读