feat(hub-guide): expand plugin with 26 best-practice skills, hooks, and references

Transform hub-guide from a single-skill Hub monorepo guide into a
comprehensive programming best-practice plugin covering all situations.

Skills (26):
- Core: engineering-principles, clean-code, clean-architecture,
  design-patterns, testing, error-handling, security, api-design,
  git-workflow, documentation, logging-observability, performance
- Languages: typescript, python, rust, go
- Frameworks: react-frontend, elysiajs, hono-backend, drizzle-database, nextjs
- Infrastructure: docker, ci-cd, monitoring
- Monorepo: monorepo, hub-guide (existing)

Hooks:
- SessionStart: auto-detect project type and activate relevant skills
- PreToolUse (Write|Edit): inject language-specific rules per file type

Reference files for deep dives:
- clean-architecture/references/solid.md (SOLID + component principles)
- design-patterns/references/catalog.md (full GoF catalog with examples)
- testing/references/mocks.md (test double taxonomy)

Restructure plugin to modern skills/ directory format.
This commit is contained in:
asepharyana
2026-07-25 11:35:16 +07:00
parent ecdce01387
commit e513cddd68
36 changed files with 4950 additions and 16 deletions
+195
View File
@@ -0,0 +1,195 @@
---
name: react-frontend
description: React and frontend best practices — component patterns, hooks, state management, TanStack Query, React Router, performance, and testing. Use when building React components, designing state management, or whenever the user mentions "React," "hooks," "state management," "component," "JSX," "TanStack Query," "React Router," "Zustand," "Vite," "Next.js," or "Frontend."
---
# React Frontend Best Practices
## Component Patterns
### Composition over Inheritance
```typescript
// ✅ Prefer composition
function Layout({ sidebar, children }: { sidebar: ReactNode; children: ReactNode }) {
return <div className="layout">{sidebar}<main>{children}</main></div>;
}
// ❌ Avoid inheritance patterns in React
```
### Container/Presentational Separation
```typescript
// Container: manages state, data fetching, business logic
function UserProfileContainer() {
const { data: user } = useUserQuery(userId);
const { mutate: update } = useUpdateUserMutation();
return <UserProfile user={user!} onUpdate={update} />;
}
// Presentational: pure rendering, props-only
function UserProfile({ user, onUpdate }: UserProfileProps) {
return <div>{user.name} <button onClick={onUpdate}>Edit</button></div>;
}
```
### Custom Hooks for Logic Extraction
```typescript
// Extract reusable logic into custom hooks
function useUserPermissions(userId: string) {
const { data: user } = useUserQuery(userId);
return useMemo(() => ({
isAdmin: user?.role === 'admin',
canEdit: user?.role === 'admin' || user?.role === 'editor',
canDelete: user?.role === 'admin',
}), [user]);
}
```
## Hooks Rules
- **Only call hooks at the top level** — not in conditions, loops, or callbacks.
- **Only call hooks from React functions** — component or custom hook.
- **Deps array matches reality** — include all values used inside.
- **`useMemo`** for expensive computations. **`useCallback`** for stable references.
- **`useEffect`** is for synchronization, not lifecycle. If you can compute from state, do it.
```typescript
// ❌ Unnecessary effect
const [fullName, setFullName] = useState('');
useEffect(() => { setFullName(`${first} ${last}`); }, [first, last]);
// ✅ Derived state
const fullName = `${first} ${last}`;
```
## Data Fetching (TanStack Query)
```typescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// Query
function useUserQuery(id: string) {
return useQuery({
queryKey: ['users', id],
queryFn: () => api.users.get({ params: { id } }),
staleTime: 30_000, // 30s before refetch
gcTime: 5 * 60_000, // 5min cache
});
}
// Mutation with optimistic update
function useUpdateUserMutation() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: UpdateUserInput) => api.users.update({ body: data }),
onMutate: async (newUser) => {
await queryClient.cancelQueries({ queryKey: ['users', newUser.id] });
const previous = queryClient.getQueryData(['users', newUser.id]);
queryClient.setQueryData(['users', newUser.id], newUser);
return { previous };
},
onError: (_, __, context) => {
queryClient.setQueryData(['users', context.previous.id], context.previous);
},
onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
});
}
```
**Rules:**
- `staleTime` for read-through caching. Default 0 = refetch on mount.
- `gcTime` for garbage collection of unused data.
- `onMutate`/`onError`/`onSettled` for optimistic updates.
- Queries over custom fetch + useEffect in all cases.
## State Management Selection
| Need | Solution |
|------|----------|
| Server state | TanStack Query |
| URL state | React Router / TanStack Router |
| Form state | React Hook Form + Zod |
| Client state (global) | Zustand or Context |
| Client state (local) | `useState` / `useReducer` |
| Component communication | Props / lifting state up |
```typescript
// Zustand — lightweight, no boilerplate
import { create } from 'zustand';
interface UIStore {
sidebarOpen: boolean;
toggleSidebar: () => void;
}
const useUIStore = create<UIStore>((set) => ({
sidebarOpen: true,
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
}));
```
## Routing (TanStack Router / React Router)
```typescript
// TanStack Router — type-safe, modern
const router = createRouter({
routeTree: rootRoute.addChildren([
indexRoute,
usersRoute.addChildren([userRoute, userProfileRoute]),
]),
});
```
- **File-based routing** (Next.js App Router, Vite/Router) for simpler projects.
- **Type-safe routers** (TanStack Router) for larger apps.
- **Lazy load** route components — `React.lazy(() => import('./routes/Dashboard'))`.
## Performance
- **Virtual lists** — `@tanstack/react-virtual` for 100+ items.
- **React.memo** sparingly — only for components that re-render often with same props.
- **`useMemo` for expensive calculations** — not for every value.
- **Code splitting** — per route, per heavy component.
- **Bundle analysis** — `vite-bundle-visualizer` to find bloat.
## Testing (Vitest + Testing Library)
```typescript
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { UserProfile } from './UserProfile';
describe('UserProfile', () => {
it('renders user name', () => {
render(<UserProfile user={{ name: 'Alice' }} />);
expect(screen.getByText('Alice')).toBeInTheDocument();
});
it('calls onUpdate when edit clicked', async () => {
const onUpdate = vi.fn();
render(<UserProfile user={{ name: 'Alice' }} onUpdate={onUpdate} />);
await userEvent.click(screen.getByRole('button', { name: /edit/i }));
expect(onUpdate).toHaveBeenCalledTimes(1);
});
});
```
- **Testing Library** for user-centric tests. Never test implementation details.
- **userEvent** over `fireEvent` — simulates real user interactions.
- **Component-level** tests for behavior, not storybook-style visual tests here.
## CSS / Styling
- **Tailwind CSS** for utility-first styling. Consistent, fast, small.
- **CSS Modules** when you need scoped component styles.
- **CSS-in-JS** (styled-components, emotion) — only for dynamic theming. Prefer Tailwind.
## Anti-patterns
-`useEffect` for data fetching — use TanStack Query
- ❌ Prop drilling beyond 3 levels — compose or context
-`useState` for derived data — compute from existing state
- ❌ Direct DOM manipulation — use React refs
-`any` in component props — always type props
- ❌ Large component files — split by responsibility
-`index` as key — breaks reconciliation on reorder
-`useEffect` without deps — runs every render