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.

Zur Installation springen

Quellinformationen

Repository
tomevault-io/skills-registry
Letzte Quellaktivität
28. April 2026 um 22:53
Erkannte Sprache von SKILL.md
Englisch
Sterne
0
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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 -->
Auf GitHub ansehen