# Frontend Patterns Skill

> **React hooks, state management, performance, and accessibility.**

---

## React Hooks Best Practices

### useState

```typescript
// ✅ Lazy initialization for expensive computation
const [data, setData] = useState(() => computeExpensiveData());

// ✅ Functional updates when depending on previous state
setCount(prev => prev + 1);

// ❌ Direct mutation
setItems(items.push(newItem));  // Wrong!

// ✅ Immutable update
setItems([...items, newItem]);
```

### useEffect

```typescript
// ✅ Proper cleanup
useEffect(() => {
  const subscription = subscribe(id);
  return () => subscription.unsubscribe();
}, [id]);

// ✅ Async pattern
useEffect(() => {
  let cancelled = false;

  async function fetchData() {
    const result = await api.get(id);
    if (!cancelled) {
      setData(result);
    }
  }

  fetchData();
  return () => { cancelled = true; };
}, [id]);
```

### useMemo / useCallback

```typescript
// ✅ Expensive computation
const sortedItems = useMemo(
  () => items.slice().sort((a, b) => a.name.localeCompare(b.name)),
  [items]
);

// ✅ Stable callback for child components
const handleClick = useCallback(
  (id: string) => onSelect(id),
  [onSelect]
);

// ❌ Over-memoization (simple values)
const value = useMemo(() => a + b, [a, b]);  // Unnecessary
```

### Custom Hooks

```typescript
// Data fetching hook
function useQuery<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    let cancelled = false;

    async function fetch() {
      try {
        setLoading(true);
        const result = await api.get<T>(url);
        if (!cancelled) setData(result);
      } catch (e) {
        if (!cancelled) setError(e as Error);
      } finally {
        if (!cancelled) setLoading(false);
      }
    }

    fetch();
    return () => { cancelled = true; };
  }, [url]);

  return { data, loading, error };
}
```

---

## State Management

### Local vs Global State

| Type | Use Case | Solution |
|------|----------|----------|
| UI State | Form inputs, toggles | `useState` |
| Server State | API data | React Query/SWR |
| Global UI | Theme, sidebar | Context |
| Complex Global | Auth, cart | Zustand/Redux |

### React Query Pattern

```typescript
function UserProfile({ userId }: { userId: string }) {
  const { data: user, isLoading, error } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    staleTime: 5 * 60 * 1000,  // 5 minutes
  });

  if (isLoading) return <Skeleton />;
  if (error) return <ErrorMessage error={error} />;

  return <Profile user={user} />;
}
```

### Zustand Pattern

```typescript
interface AuthStore {
  user: User | null;
  login: (credentials: Credentials) => Promise<void>;
  logout: () => void;
}

const useAuthStore = create<AuthStore>((set) => ({
  user: null,
  login: async (credentials) => {
    const user = await api.login(credentials);
    set({ user });
  },
  logout: () => set({ user: null }),
}));
```

---

## Performance

### Code Splitting

```typescript
// Route-based splitting
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </Suspense>
  );
}
```

### Virtualization

```typescript
import { FixedSizeList } from 'react-window';

function VirtualList({ items }: { items: Item[] }) {
  return (
    <FixedSizeList
      height={400}
      width="100%"
      itemCount={items.length}
      itemSize={50}
    >
      {({ index, style }) => (
        <div style={style}>
          {items[index].name}
        </div>
      )}
    </FixedSizeList>
  );
}
```

### Image Optimization

```typescript
// Next.js Image
import Image from 'next/image';

<Image
  src="/hero.jpg"
  alt="Hero image"
  width={1200}
  height={600}
  priority  // Above-the-fold images
  placeholder="blur"
/>
```

---

## Accessibility (a11y)

### Semantic HTML

```typescript
// ✅ Semantic
<nav aria-label="Main navigation">
  <ul>
    <li><a href="/home">Home</a></li>
  </ul>
</nav>

// ❌ Div soup
<div onClick={() => navigate('/home')}>Home</div>
```

### ARIA Labels

```typescript
// Button with icon only
<button aria-label="Close dialog">
  <XIcon />
</button>

// Loading state
<button aria-busy={isLoading} disabled={isLoading}>
  {isLoading ? 'Saving...' : 'Save'}
</button>
```

### Keyboard Navigation

```typescript
function Menu() {
  const handleKeyDown = (e: KeyboardEvent) => {
    switch (e.key) {
      case 'ArrowDown':
        focusNext();
        break;
      case 'ArrowUp':
        focusPrevious();
        break;
      case 'Escape':
        closeMenu();
        break;
    }
  };

  return <ul role="menu" onKeyDown={handleKeyDown}>...</ul>;
}
```

---

## Related Skills

- [coding-standards](../coding-standards/) - TypeScript standards
- [tdd-workflow](../tdd-workflow/) - Testing React components
