关于 studio-best-practices
面向 Supabase Studio TypeScript 和 React 开发者的代码规范指南,涵盖组件组织、数据获取、状态管理、性能与测试实践。
主要功能
- 01描述性布尔变量命名
- 02组件拆分与组织建议
- 03使用 TanStack Query 获取数据
- 04保持状态局部并规范表单管理
- 05TypeScript 边界校验与测试建议
适用场景
- 01整理 Studio 组件结构
- 02实现查询的加载与错误状态
- 03规范表单状态及事件处理
- 04编写可测试的纯逻辑函数
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ supabase/ supabase --skill studio-best-practices安装后在 Claude Code 中输入/studio-best-practices确认已加载。
详细介绍
Studio 最佳实践
适用于 apps/studio/**/*.{ts,tsx}。
布尔值命名
使用描述性前缀——从现有状态推导,而不是单独存储:
is— 状态/身份:isLoading、isPaused、isNewRecordhas— 拥有:hasPermission、hasDatacan— 能力:canUpdateColumns、canDeleteshould— 条件行为:shouldFetch、shouldRender
将复杂条件提取为命名变量:
// ❌ inline multi-condition
{
!isSchemaLocked && isTableLike(selectedTable) && canUpdateColumns && !isLoading && <Button />
}
// ✅ named variable
const canShowAddButton =
!isSchemaLocked && isTableLike(selectedTable) && canUpdateColumns && !isLoading
{
canShowAddButton && <Button />
}
推导布尔值——不要存储:
// ❌ stored derived state
const [isFormValid, setIsFormValid] = useState(false)
useEffect(() => {
setIsFormValid(name.length > 0 && email.includes('@'))
}, [name, email])
// ✅ derived
const isFormValid = name.length > 0 && email.includes('@')
组件结构
请参阅 vercel-composition-patterns 技能,了解复合组件和组合模式。
将组件控制在 200–300 行以内。遇到以下情况时进行拆分:
- 存在多个不同的 UI 区块
- 条件渲染复杂
- 有多个互不相关的
useState调用 - 代码难以一眼看懂
将子组件与父组件放在同一目录中。避免使用 barrel 重新导出文件。
将重复的 JSX 模式提取为小型组件。
数据获取
所有数据获取都使用 TanStack Query(React Query)。请参阅 studio-queries 技能,了解查询/变更模式;参阅 studio-error-handling 技能,了解错误显示规范。
加载 / 错误 / 成功模式
顶层:
const { data, error, isLoading, isError, isSuccess } = useQuery(...)
if (isLoading) return <GenericSkeletonLoader />
if (isError) return <AlertError error={error} subject="Failed to load data" />
if (isSuccess && data.length === 0) return <EmptyState />
return <DataDisplay data={data} />
使用提前返回,避免深层嵌套的条件判断。
内联:
<div>
{isLoading && <InlineLoader />}
{isError && <InlineError error={error} />}
{isSuccess && data.length === 0 && <EmptyState />}
{isSuccess && data.length > 0 && <DataDisplay data={data} />}
</div>
状态管理
尽可能将状态保留在局部;仅在需要时才提升状态。
使用 react-hook-form 对相关表单状态进行分组,而不是调用多个 useState。请参阅 studio-ui-patterns 技能,了解表单布局和组件规范。
// ❌ multiple related useState
const [name, setName] = useState('')
const [email, setEmail] = useState('')
// ✅ grouped with react-hook-form
const form = useForm<FormValues>({ defaultValues: { name: '', email: '' } })
自定义 Hook
将复杂或可复用的逻辑提取到 Hook 中。返回对象,而不是数组:
// ❌ array return (hard to extend)
return [value, toggle]
// ✅ object return
return { value, toggle, setTrue, setFalse }
事件处理器
- 属性回调:使用
on前缀(onClose、onSave) - 内部处理器:使用
handle前缀(handleSubmit、handleCancel)
传递给经过 memoization 的子组件的处理器应使用 useCallback;避免不必要的内联箭头函数。
条件渲染
// Simple show/hide
<>{isVisible && <Component />}</>
// Binary choice
<>{isLoading ? <Spinner /> : <Content />}</>
// Multiple conditions — use early returns, not nested ternaries
if (isLoading) return <Spinner />
if (isError) return <Error />
return <Content />
性能
只有在确实存在计算开销时(经过衡量,而非假设)才使用 useMemo。不要对所有内容都使用它——只有在通过测量确认存在性能问题,或需要向经过 memoization 的子组件传递值时,才进行优化。
TypeScript
显式定义属性接口。对于复杂状态,使用判别联合:
type AsyncState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error }
避免使用 as any / as Type 类型断言。在边界处使用 zod 进行验证:
// ❌ type cast
const user = apiResponse as User
// ✅ zod parse
const user = userSchema.parse(apiResponse)
// or safe:
const result = userSchema.safeParse(apiResponse)
测试
将逻辑提取到 .utils.ts 纯函数中,并进行全面测试。请参阅 studio-testing 技能,了解完整的测试策略和决策树。