⚛️ React Fundamentals
📋 Overview
React 18 introduces several key improvements that we leverage in the LIPAIX frontend. This page covers React fundamentals, modern patterns, and best practices.
🚀 React 18 Features
⚡ Concurrent Features
- Automatic Batching - Multiple state updates are batched together
- Transitions - Mark updates as non-urgent for better performance
- Suspense - Handle loading states gracefully
🎣 Hooks Best Practices
tsx
// Custom hook for managing event data
export function useEvents() {
const [events, setEvents] = useState<Event[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
const fetchEvents = async () => {
try {
setLoading(true)
const response = await fetch('/api/events')
const data = await response.json()
setEvents(data)
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to fetch events')
} finally {
setLoading(false)
}
}
fetchEvents()
}, [])
return { events, loading, error }
}🧩 Component Patterns
We follow several key patterns for maintainable React components:
- Compound Components - Related components that work together
- Render Props - Flexible component composition
- Higher-Order Components - Cross-cutting concerns
- Custom Hooks - Reusable logic extraction
🗃️ State Management
🏠 Local State
- useState for simple component state
- useReducer for complex state logic
- useContext for sharing state across components
🌍 Global State
- Zustand for lightweight global state
- React Query for server state management
- Local Storage for persistent user preferences
⚡ Performance Optimization
🔒 React.memo
tsx
const EventCard = React.memo(({ event }: { event: Event }) => {
return (
<div className="event-card">
<h3>{event.title}</h3>
<p>{event.description}</p>
</div>
)
})💾 useMemo & useCallback
tsx
const EventList = ({ events, filter }: { events: Event[], filter: string }) => {
const filteredEvents = useMemo(() => {
return events.filter(event =>
event.title.toLowerCase().includes(filter.toLowerCase())
)
}, [events, filter])
const handleEventClick = useCallback((eventId: string) => {
// Handle event click
}, [])
return (
<div>
{filteredEvents.map(event => (
<EventCard
key={event.id}
event={event}
onClick={() => handleEventClick(event.id)}
/>
))}
</div>
)
}🧪 Testing Strategy
🧩 Component Testing
tsx
import { render, screen } from '@testing-library/react'
import EventCard from '../components/EventCard'
describe('EventCard', () => {
it('renders event information correctly', () => {
const event = {
id: '1',
title: 'Test Event',
description: 'Test Description',
date: '2024-01-01'
}
render(<EventCard event={event} />)
expect(screen.getByText('Test Event')).toBeInTheDocument()
expect(screen.getByText('Test Description')).toBeInTheDocument()
})
})🔗 Integration Testing
tsx
import { render, screen } from '@testing-library/react'
import EventList from '../components/EventList'
describe('EventList', () => {
it('renders multiple events', () => {
const events = [
{ id: '1', title: 'Event 1', description: 'Desc 1' },
{ id: '2', title: 'Event 2', description: 'Desc 2' }
]
render(<EventList events={events} />)
expect(screen.getByText('Event 1')).toBeInTheDocument()
expect(screen.getByText('Event 2')).toBeInTheDocument()
})
})♿ Accessibility
📋 WCAG 2.1 AA Compliance
- Semantic HTML - Proper heading hierarchy and landmarks
- Keyboard Navigation - Full keyboard accessibility
- Screen Reader Support - ARIA labels and descriptions
- Color Contrast - Minimum 4.5:1 ratio
- Focus Management - Visible focus indicators
🔧 Accessibility Examples
tsx
function AccessibleButton({ children, ...props }: ButtonProps) {
return (
<button
{...props}
className="focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
aria-label={props['aria-label'] || typeof children === 'string' ? children : undefined}
>
{children}
</button>
)
}