Skip to main content

epic-react-patterns

Guide on React patterns, performance optimization, and code quality for Epic Stack

Informações da origem

Repositório
epicweb-dev/epic-stack
Última atividade na origem
30 de janeiro de 2026 às 00:59
Idioma detectado do SKILL.md
inglês
Estrelas
5.538
Forks
463

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
epic-react-patterns
description
Guide on React patterns, performance optimization, and code quality for Epic Stack
categories
["react","performance","patterns","optimization"]
# Epic Stack: React Patterns and Guidelines ## When to use this skill Use this skill when you need to: - Write efficient React components in Epic Stack applications - Optimize performance and bundle size - Follow React Router patterns and conventions - Avoid common React anti-patterns - Implement proper code splitting - Optimize re-renders and data fetching - Use React hooks correctly ## Philosophy Following Epic Web principles: - **Make it work, make it right, make it fast** - In that order. First make it functional, then refactor for clarity, then optimize for performance. - **Pragmatism over purity** - Choose practical solutions that work well in your context rather than theoretically perfect ones. - **Optimize for sustainable velocity** - Write code that's easy to maintain and extend, not just fast to write initially. - **Do as little as possible** - Only add complexity when it provides real value. ## Patterns and conventions ### Data Fetching in React Router Epic Stack uses React Router loaders for data fetching, not `useEffect`. **✅ Good - Use loaders:** ```typescript // app/routes/users/$username.tsx export async function loader({ params }: Route.LoaderArgs) { const user = await prisma.user.findUnique({ where: { username: params.username }, }) return { user } } export default function UserRoute({ loaderData }: Route.ComponentProps) { return <div>{loaderData.user.name}</div> } ``` **❌ Avoid - Don't fetch in useEffect:** ```typescript // ❌ Don't do this export default function UserRoute({ params }: Route.ComponentProps) { const [user, setUser] = useState(null) useEffect(() => { fetch(`/api/users/${params.username}`) .then(res => res.json()) .then(setUser) }, [params.username]) return user ? <div>{user.name}</div> : <div>Loading...</div> } ``` ### Avoid useEffect for Side Effects [You Might Not Need `useEffect`](https://react.dev/learn/you-might-not-need-an-effect) Instead of using `useEffect`, use event handlers, CSS, ref callbacks, or `useSyncExternalStore`. **✅ Good - Use event handlers:** ```typescript function ProductPage({ product, addToCart }: Route.ComponentProps) { function buyProduct() { addToCart(product) showNotification(`Added ${product.name} to cart!`) } function handleBuyClick() { buyProduct() } function handleCheckoutClick() { buyProduct() navigate('/checkout') } return ( <div> <button onClick={handleBuyClick}>Buy Now</button> <button onClick={handleCheckoutClick}>Checkout</button> </div> ) } ``` **❌ Avoid - Side effects in useEffect:** ```typescript // ❌ Don't do this function ProductPage({ product, addToCart }: Route.ComponentProps) { useEffect(() => { if (product.isInCart) { showNotification(`Added ${product.name} to cart!`) } }, [product]) function handleBuyClick() { addToCart(product) } // ... } ``` **✅ Appropriate use of useEffect:** ```typescript // ✅ Good - Event listeners are appropriate useEffect(() => { const controller = new AbortController() window.addEventListener( 'keydown', (event: KeyboardEvent) => { if (event.key !== 'Escape') return // handle escape key }, { signal: controller.signal }, ) return () => { controller.abort() } }, []) ``` ### Code Splitting with React Router React Router automatically code-splits by route. Use dynamic imports for heavy components. **✅ Good - Dynamic imports:** ```typescript // app/routes/admin/dashboard.tsx import { lazy } from 'react' const AdminChart = lazy(() => import('#app/components/admin/chart.tsx')) export default function AdminDashboard() { return ( <Suspense fallback={<div>Loading chart...</div>}> <AdminChart /> </Suspense> ) } ``` ### Optimizing Re-renders **✅ Good - Memoize expensive computations:** ```typescript import { useMemo } from 'react' function UserList({ users }: { users: User[] }) { const sortedUsers = useMemo(() => { return [...users].sort((a, b) => a.name.localeCompare(b.name)) }, [users]) return ( <ul> {sortedUsers.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> ) } ``` **✅ Good - Memoize callbacks:** ```typescript import { useCallback } from 'react' function NoteEditor({ noteId, onSave }: { noteId: string; onSave: (note: Note) => void }) { const handleSave = useCallback((note: Note) => { onSave(note) }, [onSave]) return <Editor onSave={handleSave} /> } ``` **❌ Avoid - Unnecessary memoization:** ```typescript // ❌ Don't memoize simple values const count = useMemo(() => items.length, [items]) // Just use items.length directly // ❌ Don't memoize simple callbacks const handleClick = useCallback(() => { console.log('clicked') }, []) // Just define the function normally if it doesn't need memoization ``` ### Bundle Size Optimization **✅ Good - Import only what you need:** ```typescript // ✅ Import specific functions import { useSearchParams } from 'react-router' import { parseWithZod } from '@conform-to/zod' ``` **❌ Avoid - Barrel imports:** ```typescript // ❌ Don't import entire libraries if you only need one thing import * as ReactRouter from 'react-router' import * as Conform from '@conform-to/zod' ``` ### Form Handling with Conform **✅ Good - Use Conform for forms:** ```typescript import { useForm, getFormProps } from '@conform-to/react' import { parseWithZod } from '@conform-to/zod' import { Form } from 'react-router' const SignupSchema = z.object({ email: z.string().email(), password: z.string().min(6), }) export default function SignupRoute({ actionData }: Route.ComponentProps) { const [form, fields] = useForm({ id: 'signup-form', lastResult: actionData?.result, onValidate({ formData }) { return parseWithZod(formData, { schema: SignupSchema }) }, }) return ( <Form method="POST" {...getFormProps(form)}> {/* form fields */} </Form> ) } ``` ### Component Composition **✅ Good - Compose components:** ```typescript function UserProfile({ user }: { user: User }) { return ( <Card> <UserHeader user={user} /> <UserDetails user={user} /> <UserActions userId={user.id} /> </Card> ) } ``` **❌ Avoid - Large monolithic components:** ```typescript // ❌ Don't put everything in one component function UserProfile({ user }: { user: User }) { return ( <div className="card"> <div className="header"> <img src={user.avatar} alt={user.name} /> <h1>{user.name}</h1> </div> <div className="details"> <p>{user.email}</p> <p>{user.bio}</p> </div> <div className="actions"> <button>Edit</button> <button>Delete</button> </div> </div> ) } ``` ### Error Boundaries **✅ Good - Use error boundaries:** ```typescript // app/routes/users/$username.tsx export function ErrorBoundary() { return ( <GeneralErrorBoundary statusHandlers={{ 404: ({ params }) => ( <p>User "{params.username}" not found</p> ), }} /> ) } ``` ### TypeScript Guidelines **✅ Good - Type props explicitly:** ```typescript interface UserCardProps { user: { id: string name: string email: string } onEdit?: (userId: string) => void } function UserCard({ user, onEdit }: UserCardProps) { return ( <div> <h2>{user.name}</h2> <p>{user.email}</p> {onEdit && <button onClick={() => onEdit(user.id)}>Edit</button>} </div> ) } ``` **✅ Good - Use Route types:** ```typescript import type { Route } from './+types/users.$username' export async function loader({ params }: Route.LoaderArgs) { // params is type-safe! const user = await prisma.user.findUnique({ where: { username: params.username }, }) return { user } } export default function UserRoute({ loaderData }: Route.ComponentProps) { // loaderData is type-safe! return <div>{loaderData.user.name}</div> } ``` ### Loading States **✅ Good - Use React Router's pending states:** ```typescript import { useNavigation } from 'react-router' function NoteForm() { const navigation = useNavigation() const isSubmitting = navigation.state === 'submitting' return ( <Form method="POST"> <button type="submit" disabled={isSubmitting}> {isSubmitting ? 'Saving...' : 'Save'} </button> </Form> ) } ``` ### Preventing Data Fetching Waterfalls React Router loaders can prevent waterfalls by fetching data in parallel. **❌ Avoid - Sequential data fetching (waterfall):** ```typescript // ❌ Don't do this - creates a waterfall export async function loader({ params }: Route.LoaderArgs) { const user = await prisma.user.findUnique({ where: { username: params.username }, }) // Second fetch waits for first to complete const notes = await prisma.note.findMany({ where: { ownerId: user.id }, }) return { user, notes } } ``` **✅ Good - Parallel data fetching:** ```typescript // ✅ Fetch data in parallel export async function loader({ params }: Route.LoaderArgs) { const user = await prisma.user.findUnique({ where: { username: params.username }, select: { id: true, username: true, name: true }, }) // Fetch notes in parallel with user data const [notes, stats] = await Promise.all([ user ? prisma.note.findMany({ where: { ownerId: user.id }, select: { id: true, title: true, updatedAt: true }, }) : Promise.resolve([]), user ? prisma.note.count({ where: { ownerId: user.id } }) : Promise.resolve(0), ]) return { user, notes, stats } } ``` **✅ Good - Nested route parallel loading:** ```typescript // Parent route loader // app/routes/users/$username.tsx export async function loader({ params }: Route.LoaderArgs) { const user = await prisma.user.findUnique({ where: { username: params.username }, select: { id: true, username: true, name: true }, }) return { user } } // Child route loader runs in parallel // app/routes/users/$username/notes.tsx export async function loader({ params }: Route.LoaderArgs) { const user = await prisma.user.findUnique({ where: { username: params.username }, select: { id: true },
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub