Skip to content

⚛️ 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:

  1. Compound Components - Related components that work together
  2. Render Props - Flexible component composition
  3. Higher-Order Components - Cross-cutting concerns
  4. 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>
  )
}

Released under the MIT License.