一键导入
mobile-anti-patterns
Common mistakes in React Native/Expo development with this codebase. Anti-patterns to avoid. Load when code review or debugging.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Common mistakes in React Native/Expo development with this codebase. Anti-patterns to avoid. Load when code review or debugging.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Project UI design system — read this before building any page or component. Defines visual style, color tokens, typography, and constraints.
Design tokens, Tailwind classes, card patterns, spacing, colors, radius, typography, gamification patterns (Duolingo). Load when working on UI, components, styling.
Phân tích và refactor codebase. Load khi cần review code structure, tìm vấn đề thiết kế, tách component, dọn dead code.
Tạo, cập nhật, hoặc implement RFC cho frontend-v2. Use when user says 'tạo rfc', 'viết rfc', 'implement rfc', 'update rfc', 'rfc status', or when a cross-cutting UI/UX change needs formal spec before coding.
TanStack Query patterns, data fetching, cache invalidation, route loaders. Load when working with server state, queries, mutations.
TypeScript patterns, type conventions, naming, code style. Load when writing complex types, reviewing code style, or refactoring.
| name | mobile-anti-patterns |
| description | Common mistakes in React Native/Expo development with this codebase. Anti-patterns to avoid. Load when code review or debugging. |
Wrong: payload.prompt as string
Right: Discriminated union + switch or === check. TS auto-narrows.
Wrong: tokens.getRefresh()!
Right: tokens.getRefresh() ?? "" or early return.
Wrong: .json<{ data: { id: string; name: string } }>()
Right: Define type in types.ts, use .json<ApiResponse<MyType>>().
Wrong:
if (!isAuthenticated) {
router.replace("/(auth)/login");
return null;
}
Right:
useEffect(() => {
if (!isAuthenticated) {
router.replace("/(auth)/login");
}
}, [isAuthenticated]);
if (!isAuthenticated) return null;
Wrong: Each component does try/catch + Alert.alert().
Right: Global QueryClient onError handles 401 → logout. Components use Alert only for user-friendly messages.
Wrong:
const handleSubmit = async () => {
await api.post("/submit", data);
router.push("/result");
};
Right:
const mutation = useMutation({
mutationFn: (d) => api.post("/submit", d),
onSuccess: () => router.push("/result"),
});
const handleSubmit = () => mutation.mutate(data);
Why: QueryClient onError only catches errors from queries + mutations, not plain async calls.
Wrong: SecureStore.setItemAsync("vstep_access_token", token) in component.
Right: Auth store (src/lib/auth.ts) is the only place that calls SecureStore.*. External code calls store actions.
Wrong: profile?.nickname inside authenticated context.
Right: useAuth() returns typed state. Use profile!.nickname only if guard is certain, better: use discriminated union.
Wrong:
{SKILLS.map(skill => (
<DepthCard key={skill} variant="skill" skillColor={skill.color}>
<Text>{skill.label}</Text>
</DepthCard>
))}
Right: Write each card explicitly. Easier to read, style individually.
<DepthCard variant="skill" skillColor={colors.light.skillListening}>
<Text>Nghe</Text>
</DepthCard>
<DepthCard variant="skill" skillColor={colors.light.skillReading}>
<Text>Đọc</Text>
</DepthCard>
Wrong: Component receives 6+ separate props. Right: Group related props into shared type. Props ≤ 3.
Wrong: Route page 140+ lines with hooks, handlers, sub-components inline.
Right: Route page ≤ 80 lines, only compose. Logic in src/hooks/, UI in src/components/.
Wrong: color: "#58CC02", padding: 16, fontSize: 16
Right: colors.light.primary, spacing.base, fontSize.base
Wrong: Hardcoded exam/practice data in components. Right: Data from API (TanStack Query). If API doesn't exist, create endpoint first.
Wrong: Using native audio controls (<audio controls />).
Right: Hidden audio + custom progress bar. VSTEP standard: audio plays once only.
Wrong: AsyncStorage.setItem("token", value)
Right: expo-secure-store for sensitive data. Only use AsyncStorage for non-sensitive cached data.