Skip to main content

react-component-structure

Best practices for structuring React components - function declarations, file organization, and Single Responsibility Principle Use when this capability is needed.

Ir para a instalação

Informações da origem

Repositório
tomevault-io/skills-registry
Última atividade na origem
28 de abril de 2026 às 22:53
Idioma detectado do SKILL.md
inglês
Estrelas
0
Forks
0

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.

Explorador de arquivos
2 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
react-component-structure
description
Best practices for structuring React components - function declarations, file organization, and Single Responsibility Principle Use when this capability is needed.
metadata
{"author":"alexandrebenkendorf"}
# React Component Structure Best practices for structuring React components for readability, testability, and maintainability. --- ## Function Declarations vs Const **Always use function declarations** for components, not const with arrow functions. ### ❌ Don't ```tsx const UserProfile = ({ name, email }: Props) => { return ( <div> <h1>{name}</h1> <p>{email}</p> </div> ) } // Or worse const UserProfile: React.FC<Props> = ({ name, email }) => { return ( <div> <h1>{name}</h1> <p>{email}</p> </div> ) } ``` ### ✅ Do ```tsx function UserProfile({ name, email }: Props) { return ( <div> <h1>{name}</h1> <p>{email}</p> </div> ); } ``` ### Why? 1. **Better stack traces**: Function declarations show the component name clearly in error stacks 2. **Hoisting**: Can reference components before they're defined in the file 3. **Avoid React.FC pitfalls**: - Implicitly includes `children` (often unwanted) - Return type is `ReactNode` which includes `undefined` (allows accidental `undefined` returns) - Incompatible with some generics patterns - Adds unnecessary abstraction 4. **Clearer intent**: Function declarations signal "this is a component" 5. **TypeScript inference**: Props type is explicit and clear --- ## Single Responsibility Principle Components should do **one thing well**. Avoid mixing concerns or creating "god components". > 📖 **For comprehensive SOLID principles including SRP, see:** [code-standards/rules/solid-principles.md](../code-standards/rules/solid-principles.md#s---single-responsibility-principle) **In React context, SRP means:** - Each component has one clear purpose - Easy to name descriptively - Easy to test in isolation - Changes for one reason only ### ❌ Don't: Multiple helpers before component ```tsx // File: UserProfile.tsx function formatDate(date: Date): string { return new Intl.DateTimeFormat('nl-NL').format(date); } function calculateAge(birthDate: Date): number { const today = new Date(); const age = today.getFullYear() - birthDate.getFullYear(); return age; } function validateEmail(email: string): boolean { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); } function formatPhoneNumber(phone: string): string { return phone.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3'); } function capitalizeWords(str: string): string { return str.replace(/\b\w/g, (l) => l.toUpperCase()); } // Finally, the component (after 5 helpers!) function UserProfile({ user }: Props) { return ( <div> <h1>{capitalizeWords(user.name)}</h1> <p>Age: {calculateAge(user.birthDate)}</p> <p>Email: {user.email}</p> <p>Phone: {formatPhoneNumber(user.phone)}</p> </div> ); } ``` ### ✅ Do: Extract helpers to utility files ```tsx // File: UserProfile.tsx import { calculateAge, formatDate } from '@/utils/date-helpers'; import { formatPhoneNumber } from '@/utils/formatters'; import { capitalizeWords } from '@/utils/string-helpers'; import { validateEmail } from '@/utils/validators'; function UserProfile({ user }: Props) { return ( <div> <h1>{capitalizeWords(user.name)}</h1> <p>Age: {calculateAge(user.birthDate)}</p> <p>Email: {user.email}</p> <p>Phone: {formatPhoneNumber(user.phone)}</p> </div> ); } ``` ### ✅ Alternative: Place simple helpers after component For **component-specific** helpers that are short (1-3 lines): ```tsx // File: UserCard.tsx function UserCard({ user, onEdit }: Props) { const displayName = formatDisplayName(user.firstName, user.lastName); const initials = getInitials(user.firstName, user.lastName); return ( <div> <Avatar>{initials}</Avatar> <h2>{displayName}</h2> <button onClick={() => onEdit(user.id)}>Edit</button> </div> ); } // Simple, component-specific helpers placed AFTER function formatDisplayName(first: string, last: string): string { return `${first} ${last}`; } function getInitials(first: string, last: string): string { return `${first[0]}${last[0]}`.toUpperCase(); } ``` --- ## Component Organization Pattern **Recommended file structure:** ```tsx // 1. Imports (external first, then internal) import { formatCurrency } from '@/utils/formatters'; import { useEffect, useState } from 'react'; // 2. Constants (if needed) const DISCOUNT_THRESHOLD = 100; // 3. Types/Interfaces (as close as possible to component) interface ProductCardProps { product: Product; onAddToCart: (id: string) => void; } // 4. Main component function ProductCard({ product, onAddToCart }: ProductCardProps) { const [quantity, setQuantity] = useState(1); const discountedPrice = calculateDiscount(product.price, quantity); return ( <div> <h3>{product.name}</h3> <p>{formatCurrency(discountedPrice)}</p> <button onClick={() => onAddToCart(product.id)}>Add to Cart</button> </div> ); } // 5. Component-specific helpers (if simple and tightly coupled) function calculateDiscount(price: number, qty: number): number { return qty >= DISCOUNT_THRESHOLD ? price * 0.9 : price; } // 6. Export export { ProductCard }; ``` --- ## When to Extract ### Extract to separate file when: - ✅ Helper is reused across 2+ components - ✅ Logic is complex (>10 lines) - ✅ Helper is domain logic (business rules) - ✅ Helper needs separate testing ### Keep in component file when: - ✅ Used only in this component - ✅ Simple (1-5 lines) - ✅ Tightly coupled to component logic - ✅ Pure formatting/transformation --- ## Anti-Patterns ### ❌ Don't: God component ```tsx function Dashboard() { // 300 lines of logic handling: // - User authentication // - Data fetching // - Filtering // - Sorting // - Pagination // - Charts rendering // - Form handling return <>{/* 200 lines of JSX */}</>; } ``` ### ✅ Do: Compose smaller components ```tsx function Dashboard() { return ( <DashboardLayout> <UserHeader /> <FilterControls /> <DataTable /> <ChartSection /> </DashboardLayout> ); } ``` ### ❌ Don't: Mixing presentation and logic ```tsx function ProductList() { // Data fetching, filtering, sorting, pagination const [products, setProducts] = useState([]); const [filters, setFilters] = useState({}); // ... 50 lines of logic return <>{/* Presentation */}</>; } ``` ### ✅ Do: Separate concerns ```tsx function ProductList() { const { products, isLoading } = useProducts(); // Custom hook for logic if (isLoading) { return <LoadingSpinner />; } return <ProductGrid products={products} />; } ``` --- ## Summary | Aspect | Guideline | | --------------------- | ---------------------------------------------------------- | | Component syntax | Function declarations, not const | | React.FC | Avoid (unnecessary, has pitfalls) | | Helpers | Extract to utils if reused or complex | | Component size | Keep focused (~50-100 lines) | | Organization | Imports → Constants → Props → Component → Helpers → Export | | Props location | Define immediately before component | | Single Responsibility | One component, one purpose | **Principle:** Components should be easy to understand at a glance. If you have to scroll past 5 helpers to find the component, refactor. --- > Converted and distributed by [TomeVault](https://tomevault.io/claim/alexandrebenkendorf) — claim your Tome and manage your conversions. <!-- tomevault:4.0:skill_md:2026-04-13 -->
Ver no GitHub