--- name: react-patterns version: 2.0.0 description: | Comprehensive React 19 patterns expert covering Server Components, Actions, use() hook, useOptimistic, useFormStatus, useFormState, React Compiler, concurrent features, Suspense, and modern TypeScript development. 트리거: "React", "컴포넌트", "hooks", "JSX/TSX", "상태 관리" 안티-트리거: "Vue", "Svelte", "Angular", "백엔드" user-invocable: false language: typescript,javascript,tsx,jsx framework: react license: MIT allowed-tools: Read, Write, Edit, Bash, Grep, Glob tags: [react, react-19, typescript, javascript, jsx, tsx, hooks, server-components, actions, suspense, concurrent-rendering, react-compiler, use-optimistic, form-actions] category: frontend subcategories: [components, hooks, state-management, performance, server-components, forms, testing] --- # React Development Patterns Expert guide for building modern React 19 applications with new concurrent features, Server Components, Actions, and advanced patterns. ## When to Use - Building React 19 components with TypeScript/JavaScript - Managing component state with useState and useReducer - Handling side effects with useEffect - Optimizing performance with useMemo and useCallback - Creating custom hooks for reusable logic - Implementing component composition patterns - Working with refs using useRef - Using React 19's new features (use(), useOptimistic, useFormStatus) - Implementing Server Components and Actions - Working with Suspense and concurrent rendering - Building forms with new form hooks ## Core Hooks Patterns ### useState - State Management Basic state declaration and updates: ```typescript import { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( ); } ``` State with initializer function (expensive computation): ```typescript const [state, setState] = useState(() => { const initialState = computeExpensiveValue(); return initialState; }); ``` Multiple state variables: ```typescript function UserProfile() { const [name, setName] = useState(''); const [age, setAge] = useState(0); const [email, setEmail] = useState(''); return (
setName(e.target.value)} /> setAge(Number(e.target.value))} /> setEmail(e.target.value)} />
); } ``` ### useEffect - Side Effects Basic effect with cleanup: ```typescript import { useEffect } from 'react'; function ChatRoom({ roomId }: { roomId: string }) { useEffect(() => { const connection = createConnection(roomId); connection.connect(); // Cleanup function return () => { connection.disconnect(); }; }, [roomId]); // Dependency array return
Connected to {roomId}
; } ``` Effect with multiple dependencies: ```typescript function ChatRoom({ roomId, serverUrl }: { roomId: string; serverUrl: string }) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); }, [roomId, serverUrl]); // Re-run when either changes return

Welcome to {roomId}

; } ``` Effect for subscriptions: ```typescript function StatusBar() { const [isOnline, setIsOnline] = useState(true); useEffect(() => { function handleOnline() { setIsOnline(true); } function handleOffline() { setIsOnline(false); } window.addEventListener('online', handleOnline); window.addEventListener('offline', handleOffline); return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); }; }, []); // Empty array = run once on mount return

{isOnline ? '✅ Online' : '❌ Disconnected'}

; } ``` ### useRef - Persistent References Storing mutable values without re-renders: ```typescript import { useRef } from 'react'; function Timer() { const intervalRef = useRef(null); const startTimer = () => { intervalRef.current = setInterval(() => { console.log('Tick'); }, 1000); }; const stopTimer = () => { if (intervalRef.current) { clearInterval(intervalRef.current); } }; return ( <> ); } ``` DOM element references: ```typescript function TextInput() { const inputRef = useRef(null); const focusInput = () => { inputRef.current?.focus(); }; return ( <> ); } ``` ## Custom Hooks Pattern Extract reusable logic into custom hooks: ```typescript // useOnlineStatus.ts import { useState, useEffect } from 'react'; export function useOnlineStatus() { const [isOnline, setIsOnline] = useState(true); useEffect(() => { function handleOnline() { setIsOnline(true); } function handleOffline() { setIsOnline(false); } window.addEventListener('online', handleOnline); window.addEventListener('offline', handleOffline); return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); }; }, []); return isOnline; } // Usage in components function StatusBar() { const isOnline = useOnlineStatus(); return

{isOnline ? '✅ Online' : '❌ Disconnected'}

; } function SaveButton() { const isOnline = useOnlineStatus(); return ( ); } ``` Custom hook with parameters: ```typescript // useChatRoom.ts import { useEffect } from 'react'; interface ChatOptions { serverUrl: string; roomId: string; } export function useChatRoom({ serverUrl, roomId }: ChatOptions) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); }, [serverUrl, roomId]); } // Usage function ChatRoom({ roomId }: { roomId: string }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); useChatRoom({ serverUrl, roomId }); return ( <> setServerUrl(e.target.value)} />

Welcome to {roomId}

); } ``` ## Component Composition Patterns ### Props and Children Basic component with props: ```typescript interface ButtonProps { variant?: 'primary' | 'secondary'; size?: 'sm' | 'md' | 'lg'; onClick?: () => void; children: React.ReactNode; } function Button({ variant = 'primary', size = 'md', onClick, children }: ButtonProps) { return ( ); } ``` Composition with children: ```typescript interface CardProps { children: React.ReactNode; className?: string; } function Card({ children, className = '' }: CardProps) { return (
{children}
); } // Usage function UserProfile() { return (

John Doe

Software Engineer

); } ``` ### Lifting State Up Shared state between siblings: ```typescript function Parent() { const [activeIndex, setActiveIndex] = useState(0); return ( <> setActiveIndex(0)} > Panel 1 content setActiveIndex(1)} > Panel 2 content ); } interface PanelProps { isActive: boolean; onShow: () => void; children: React.ReactNode; } function Panel({ isActive, onShow, children }: PanelProps) { return (
{isActive &&
{children}
}
); } ``` ## Performance Optimization ### Avoid Unnecessary Effects ❌ Bad - Using effect for derived state: ```typescript function TodoList({ todos }: { todos: Todo[] }) { const [visibleTodos, setVisibleTodos] = useState([]); useEffect(() => { setVisibleTodos(todos.filter(t => !t.completed)); }, [todos]); // Unnecessary effect return
    {/* ... */}
; } ``` ✅ Good - Compute during render: ```typescript function TodoList({ todos }: { todos: Todo[] }) { const visibleTodos = todos.filter(t => !t.completed); // Direct computation return
    {/* ... */}
; } ``` ### useMemo for Expensive Computations ```typescript import { useMemo } from 'react'; function DataTable({ data }: { data: Item[] }) { const sortedData = useMemo(() => { return [...data].sort((a, b) => a.name.localeCompare(b.name)); }, [data]); // Only recompute when data changes return {/* render sortedData */}
; } ``` ### useCallback for Function Stability ```typescript import { useCallback } from 'react'; function Parent() { const [count, setCount] = useState(0); const handleClick = useCallback(() => { console.log('Clicked', count); }, [count]); // Recreate only when count changes return ; } ``` ## TypeScript Best Practices ### Type-Safe Props ```typescript interface UserProps { id: string; name: string; email: string; age?: number; // Optional } function User({ id, name, email, age }: UserProps) { return (

{name}

{email}

{age &&

Age: {age}

}
); } ``` ### Generic Components ```typescript interface ListProps { items: T[]; renderItem: (item: T) => React.ReactNode; } function List({ items, renderItem }: ListProps) { return (
    {items.map((item, index) => (
  • {renderItem(item)}
  • ))}
); } // Usage {user.name}} /> ``` ### Event Handlers ```typescript function Form() { const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // Handle form submission }; const handleChange = (e: React.ChangeEvent) => { console.log(e.target.value); }; return (
); } ``` ## Common Patterns ### Controlled Components ```typescript function ControlledInput() { const [value, setValue] = useState(''); return ( setValue(e.target.value)} /> ); } ``` ### Conditional Rendering ```typescript function Greeting({ isLoggedIn }: { isLoggedIn: boolean }) { return (
{isLoggedIn ? ( ) : ( )}
); } ``` ### Lists and Keys ```typescript function UserList({ users }: { users: User[] }) { return (
    {users.map(user => (
  • {user.name}
  • ))}
); } ``` ## Best Practices ### General React Best Practices 1. **Dependency Arrays**: Always specify correct dependencies in useEffect 2. **State Structure**: Keep state minimal and avoid redundant state 3. **Component Size**: Keep components small and focused 4. **Custom Hooks**: Extract complex logic into reusable custom hooks 5. **TypeScript**: Use TypeScript for type safety 6. **Keys**: Use stable IDs as keys for list items, not array indices 7. **Immutability**: Never mutate state directly 8. **Effects**: Use effects only for synchronization with external systems 9. **Performance**: Profile before optimizing with useMemo/useCallback ### React 19 Specific Best Practices 1. **Server Components**: Use Server Components for data fetching and static content 2. **Client Components**: Mark components as 'use client' only when necessary 3. **Actions**: Use Server Actions for mutations and form submissions 4. **Optimistic Updates**: Implement useOptimistic for better UX 5. **use() Hook**: Use for reading promises and context conditionally 6. **Form State**: Use useFormState and useFormStatus for complex forms 7. **Concurrent Features**: Leverage useTransition for non-urgent updates 8. **Error Boundaries**: Implement proper error handling with error boundaries ## Common Pitfalls ### General React Pitfalls ❌ **Missing Dependencies**: ```typescript useEffect(() => { // Uses 'count' but doesn't include it in deps console.log(count); }, []); // Wrong! ``` ❌ **Mutating State**: ```typescript const [items, setItems] = useState([]); items.push(newItem); // Wrong! Mutates state setItems(items); // Won't trigger re-render ``` ✅ **Correct Approach**: ```typescript setItems([...items, newItem]); // Create new array ``` ### React 19 Specific Pitfalls ❌ **Using use() outside of render**: ```typescript // Wrong! function handleClick() { const data = use(promise); // Error: use() can only be called in render } ``` ✅ **Correct usage**: ```tsx function Component({ promise }) { const data = use(promise); // Correct: called during render return
{data}
; } ``` ❌ **Forgetting 'use server' directive**: ```typescript // Wrong - missing 'use server' export async function myAction() { // This will run on the client! } ``` ✅ **Correct Server Action**: ```typescript 'use server'; // Must be at the top export async function myAction() { // Now runs on the server } ``` ❌ **Mixing Server and Client logic incorrectly**: ```tsx // Wrong - trying to use browser APIs in Server Component export default async function ServerComponent() { const width = window.innerWidth; // Error: window is not defined return
{width}
; } ``` ✅ **Correct separation**: ```tsx // Server Component for data export default async function ServerComponent() { const data = await fetchData(); return ; } // Client Component for browser APIs 'use client'; function ClientComponent({ data }) { const [width, setWidth] = useState(window.innerWidth); // Handle resize logic... return
{width}
; } ``` ## React 19 New Features ### use() Hook - Reading Resources The `use()` hook reads the value from a resource like a Promise or Context: ```typescript import { use } from 'react'; // Reading a Promise in a component function MessageComponent({ messagePromise }) { const message = use(messagePromise); return

{message}

; } // Reading Context conditionally function Button() { if (condition) { const theme = use(ThemeContext); return ; } return ; } ``` ### useOptimistic Hook - Optimistic UI Updates Manage optimistic UI updates for async operations: ```typescript import { useOptimistic } from 'react'; function TodoList({ todos, addTodo }) { const [optimisticTodos, addOptimisticTodo] = useOptimistic( todos, (state, newTodo) => [...state, newTodo] ); const handleSubmit = async (formData) => { const newTodo = { id: Date.now(), text: formData.get('text') }; // Optimistically add to UI addOptimisticTodo(newTodo); // Actually add to backend await addTodo(newTodo); }; return (
{optimisticTodos.map(todo => (
{todo.text}
))}
); } ``` ### useFormStatus Hook - Form State Access form submission status from child components: ```typescript import { useFormStatus } from 'react'; function SubmitButton() { const { pending, data } = useFormStatus(); return ( ); } function ContactForm() { return (
); } ``` ### useFormState Hook - Form State Management Manage form state with error handling: ```typescript import { useFormState } from 'react'; async function submitAction(prevState: string | null, formData: FormData) { const email = formData.get('email') as string; if (!email.includes('@')) { return 'Invalid email address'; } await submitToDatabase(email); return null; } function EmailForm() { const [state, formAction] = useFormState(submitAction, null); return (
{state &&

{state}

}
); } ``` ### Server Actions Define server-side functions for form handling: ```typescript // app/actions.ts 'use server'; import { redirect } from 'next/navigation'; import { revalidatePath } from 'next/cache'; export async function createPost(formData: FormData) { const title = formData.get('title') as string; const content = formData.get('content') as string; // Validate input if (!title || !content) { return { error: 'Title and content are required' }; } // Save to database const post = await db.post.create({ data: { title, content } }); // Update cache and redirect revalidatePath('/posts'); redirect(`/posts/${post.id}`); } ``` ### Server Components Components that run exclusively on the server: ```typescript // app/posts/page.tsx - Server Component async function PostsPage() { // Server-side data fetching const posts = await db.post.findMany({ orderBy: { createdAt: 'desc' }, take: 10 }); return (

Latest Posts

); } // Client Component for interactivity 'use client'; function PostsList({ posts }: { posts: Post[] }) { const [selectedId, setSelectedId] = useState(null); return (
    {posts.map(post => (
  • setSelectedId(post.id)} className={selectedId === post.id ? 'selected' : ''} > {post.title}
  • ))}
); } ``` ## React Compiler ### Automatic Optimization React Compiler automatically optimizes your components: ```typescript // Before React Compiler - manual memoization needed const ExpensiveComponent = memo(function ExpensiveComponent({ data, onUpdate }) { const processedData = useMemo(() => { return data.map(item => ({ ...item, computed: expensiveCalculation(item) })); }, [data]); const handleClick = useCallback((id) => { onUpdate(id); }, [onUpdate]); return (
{processedData.map(item => ( ))}
); }); // After React Compiler - no manual optimization needed function ExpensiveComponent({ data, onUpdate }) { const processedData = data.map(item => ({ ...item, computed: expensiveCalculation(item) })); const handleClick = (id) => { onUpdate(id); }; return (
{processedData.map(item => ( ))}
); } ``` ### Installation and Setup ```bash # Install React Compiler npm install -D babel-plugin-react-compiler@latest # Install ESLint plugin for validation npm install -D eslint-plugin-react-hooks@latest ``` ```javascript // babel.config.js module.exports = { plugins: [ 'babel-plugin-react-compiler', // Must run first! // ... other plugins ], }; ``` ```javascript // vite.config.js for Vite users import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [ react({ babel: { plugins: ['babel-plugin-react-compiler'], }, }), ], }); ``` ### Compiler Configuration ```javascript // babel.config.js with compiler options module.exports = { plugins: [ [ 'babel-plugin-react-compiler', { // Enable compilation for specific files target: '18', // or '19' // Debug mode for development debug: process.env.NODE_ENV === 'development' } ], ], }; // Incremental adoption with overrides module.exports = { plugins: [], overrides: [ { test: './src/components/**/*.{js,jsx,ts,tsx}', plugins: ['babel-plugin-react-compiler'] } ] }; ``` ## Advanced Server Components Patterns ### Mixed Server/Client Architecture ```typescript // Server Component for data fetching async function ProductPage({ id }: { id: string }) { const product = await fetchProduct(id); const related = await fetchRelatedProducts(id); return (
); } // Client Component for interactivity 'use client'; function ProductDetails({ product }: { product: Product }) { const [quantity, setQuantity] = useState(1); const [isAdded, setIsAdded] = useState(false); return (

{product.name}

{product.description}

${product.price}

setIsAdded(true)} /> {isAdded &&

Added to cart!

}
); } ``` ### Server Actions with Validation ```typescript 'use server'; import { z } from 'zod'; const checkoutSchema = z.object({ items: z.array(z.object({ productId: z.string(), quantity: z.number().min(1) })), shippingAddress: z.object({ street: z.string().min(1), city: z.string().min(1), zipCode: z.string().regex(/^\d{5}$/) }), paymentMethod: z.enum(['credit', 'paypal', 'apple']) }); export async function processCheckout( prevState: any, formData: FormData ) { // Extract and validate data const rawData = { items: JSON.parse(formData.get('items') as string), shippingAddress: { street: formData.get('street'), city: formData.get('city'), zipCode: formData.get('zipCode') }, paymentMethod: formData.get('paymentMethod') }; const result = checkoutSchema.safeParse(rawData); if (!result.success) { return { error: 'Validation failed', fieldErrors: result.error.flatten().fieldErrors }; } try { // Process payment const order = await createOrder(result.data); // Update inventory await updateInventory(result.data.items); // Send confirmation await sendConfirmationEmail(order); // Revalidate cache revalidatePath('/orders'); return { success: true, orderId: order.id }; } catch (error) { return { error: 'Payment failed' }; } } ``` ## Concurrent Features ### useTransition for Non-Urgent Updates ```typescript import { useTransition, useState } from 'react'; function SearchableList({ items }: { items: Item[] }) { const [query, setQuery] = useState(''); const [isPending, startTransition] = useTransition(); const [filteredItems, setFilteredItems] = useState(items); const handleChange = (e: React.ChangeEvent) => { // Update input immediately setQuery(e.target.value); // Transition the filter operation startTransition(() => { setFilteredItems( items.filter(item => item.name.toLowerCase().includes(e.target.value.toLowerCase()) ) ); }); }; return (
{isPending &&
Filtering...
}
    {filteredItems.map(item => (
  • {item.name}
  • ))}
); } ``` ### useDeferredValue for Expensive UI ```typescript import { useDeferredValue, useMemo } from 'react'; function DataGrid({ data }: { data: DataRow[] }) { const [searchTerm, setSearchTerm] = useState(''); const deferredSearchTerm = useDeferredValue(searchTerm); const filteredData = useMemo(() => { return data.filter(row => Object.values(row).some(value => String(value).toLowerCase().includes(deferredSearchTerm.toLowerCase()) ) ); }, [data, deferredSearchTerm]); return (
setSearchTerm(e.target.value)} placeholder="Search..." className={searchTerm !== deferredSearchTerm ? 'stale' : ''} />
); } ``` ## Testing React 19 Features ### Testing Server Actions ```typescript import { render, screen, fireEvent } from '@testing-library/react'; import { jest } from '@jest/globals'; import ContactForm from './ContactForm'; // Mock server action const mockSubmitForm = jest.fn(); describe('ContactForm', () => { it('submits form with server action', async () => { render(); fireEvent.change(screen.getByLabelText('Email'), { target: { value: 'test@example.com' } }); fireEvent.click(screen.getByText('Submit')); expect(mockSubmitForm).toHaveBeenCalledWith( expect.any(FormData) ); }); it('shows loading state during submission', async () => { mockSubmitForm.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100))); render(); fireEvent.click(screen.getByText('Submit')); expect(screen.getByText('Submitting...')).toBeInTheDocument(); await waitFor(() => { expect(screen.getByText('Submit')).toBeInTheDocument(); }); }); }); ``` ### Testing Optimistic Updates ```typescript import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { jest } from '@jest/globals'; import TodoList from './TodoList'; describe('useOptimistic', () => { it('shows optimistic update immediately', async () => { const mockAddTodo = jest.fn(() => new Promise(resolve => setTimeout(resolve, 100))); render( ); fireEvent.change(screen.getByPlaceholderText('Add a todo'), { target: { value: 'New todo' } }); fireEvent.click(screen.getByText('Add')); // Optimistic update appears immediately expect(screen.getByText('New todo')).toBeInTheDocument(); // Wait for actual submission await waitFor(() => { expect(mockAddTodo).toHaveBeenCalledWith({ id: expect.any(Number), text: 'New todo' }); }); }); }); ``` ## Performance Best Practices ### React Compiler Guidelines 1. **Write Standard React Code**: The compiler works best with idiomatic React patterns 2. **Avoid Manual Memoization**: Let the compiler handle useMemo, useCallback, and memo 3. **Keep Components Pure**: Avoid side effects in render 4. **Use Stable References**: Pass stable objects as props ```typescript // Good: Clean, idiomatic React function ProductCard({ product, onAddToCart }) { const [quantity, setQuantity] = useState(1); const handleAdd = () => { onAddToCart(product.id, quantity); }; return (

{product.name}

${product.price}

setQuantity(Number(e.target.value))} min="1" />
); } // Avoid: Manual optimization function ProductCard({ product, onAddToCart }) { const [quantity, setQuantity] = useState(1); const handleAdd = useCallback(() => { onAddToCart(product.id, quantity); }, [product.id, quantity, onAddToCart]); return (

{product.name}

${product.price}

); } ``` ### Server Components Best Practices 1. **Keep Server Components Server-Only**: No event handlers, hooks, or browser APIs 2. **Minimize Client Components**: Only use 'use client' when necessary 3. **Pass Data as Props**: Serialize data when passing from Server to Client 4. **Use Server Actions for Mutations**: Keep data operations on the server ```typescript // Good: Server Component for static content async function ProductPage({ id }: { id: string }) { const product = await fetchProduct(id); return (

{product.name}

{product.description}

{product.name}
); } // Client Component only for interactivity 'use client'; function AddToCartForm({ productId }: { productId: string }) { const [isAdding, setIsAdding] = useState(false); async function handleSubmit() { setIsAdding(true); await addToCart(productId); setIsAdding(false); } return (
); } ``` ## Migration Guide ### From React 18 to 19 1. **Update Dependencies**: ```bash npm install react@19 react-dom@19 ``` 2. **Adopt Server Components**: - Identify data-fetching components - Remove client-side code from Server Components - Add 'use client' directive where needed 3. **Replace Manual Optimistic Updates**: ```typescript // Before function TodoList({ todos, addTodo }) { const [optimisticTodos, setOptimisticTodos] = useState(todos); const handleAdd = async (text) => { const newTodo = { id: Date.now(), text }; setOptimisticTodos([...optimisticTodos, newTodo]); await addTodo(newTodo); }; } // After function TodoList({ todos, addTodo }) { const [optimisticTodos, addOptimisticTodo] = useOptimistic( todos, (state, newTodo) => [...state, newTodo] ); const handleAdd = async (formData) => { const newTodo = { id: Date.now(), text: formData.get('text') }; addOptimisticTodo(newTodo); await addTodo(newTodo); }; } ``` 4. **Enable React Compiler**: - Install babel-plugin-react-compiler - Remove manual memoization - Let the compiler optimize automatically ## References - React 19 Official Docs: https://react.dev/blog/2024/04/25/react-19 - React Server Components: https://react.dev/reference/rsc/server-components - React Compiler: https://react.dev/learn/react-compiler - TypeScript with React: https://react-typescript-cheatsheet.netlify.app/