# React Patterns

> Modern React patterns for React 19. Hooks, state management, component composition, performance optimization. Use when building React components.

- Skill: `majiayu000/react-patterns-14` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add majiayu000/react-patterns-14`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/react-patterns-14/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/react-patterns-14

---


# React Patterns - Modern Component Architecture

## Purpose

Expert guidance for React 19 patterns:

- **Component Design** - Composition over inheritance
- **State Management** - Local, context, and server state
- **Performance** - Memoization and optimization
- **Hooks** - Built-in and custom hooks
- **TypeScript** - Proper typing patterns

---

## Component Patterns

### 1. Compound Components

```tsx
// Composable API for complex UIs
interface TabsContextValue {
	activeTab: string;
	setActiveTab: (id: string) => void;
}

const TabsContext = createContext<TabsContextValue | null>(null);

function Tabs({ children, defaultTab }: { children: ReactNode; defaultTab: string }) {
	const [activeTab, setActiveTab] = useState(defaultTab);

	return (
		<TabsContext.Provider value={{ activeTab, setActiveTab }}>
			<div className="tabs">{children}</div>
		</TabsContext.Provider>
	);
}

function TabList({ children }: { children: ReactNode }) {
	return (
		<div className="tab-list" role="tablist">
			{children}
		</div>
	);
}

function Tab({ id, children }: { id: string; children: ReactNode }) {
	const context = useContext(TabsContext);
	if (!context) throw new Error('Tab must be used within Tabs');

	return (
		<button
			role="tab"
			aria-selected={context.activeTab === id}
			onClick={() => context.setActiveTab(id)}
		>
			{children}
		</button>
	);
}

function TabPanel({ id, children }: { id: string; children: ReactNode }) {
	const context = useContext(TabsContext);
	if (!context) throw new Error('TabPanel must be used within Tabs');

	if (context.activeTab !== id) return null;
	return <div role="tabpanel">{children}</div>;
}

// Usage
<Tabs defaultTab="home">
	<TabList>
		<Tab id="home">Home</Tab>
		<Tab id="settings">Settings</Tab>
	</TabList>
	<TabPanel id="home">Home content</TabPanel>
	<TabPanel id="settings">Settings content</TabPanel>
</Tabs>;
```

### 2. Render Props Pattern

```tsx
interface MousePosition {
	x: number;
	y: number;
}

function MouseTracker({ render }: { render: (pos: MousePosition) => ReactNode }) {
	const [position, setPosition] = useState({ x: 0, y: 0 });

	useEffect(() => {
		const handleMove = (e: MouseEvent) => {
			setPosition({ x: e.clientX, y: e.clientY });
		};
		window.addEventListener('mousemove', handleMove);
		return () => window.removeEventListener('mousemove', handleMove);
	}, []);

	return <>{render(position)}</>;
}

// Usage
<MouseTracker
	render={({ x, y }) => (
		<div>
			Mouse: {x}, {y}
		</div>
	)}
/>;
```

### 3. HOC (Higher-Order Component)

```tsx
function withAuth<P extends object>(Component: ComponentType<P>) {
	return function AuthenticatedComponent(props: P) {
		const { user, isLoading } = useAuth();

		if (isLoading) return <Loading />;
		if (!user) return <Redirect to="/login" />;

		return <Component {...props} />;
	};
}

// Usage
const ProtectedDashboard = withAuth(Dashboard);
```

---

## Hooks Patterns

### Custom Hooks

```tsx
// useLocalStorage - Persist state
function useLocalStorage<T>(key: string, initialValue: T) {
	const [storedValue, setStoredValue] = useState<T>(() => {
		if (typeof window === 'undefined') return initialValue;
		try {
			const item = window.localStorage.getItem(key);
			return item ? JSON.parse(item) : initialValue;
		} catch {
			return initialValue;
		}
	});

	const setValue = useCallback(
		(value: T | ((val: T) => T)) => {
			setStoredValue((prev) => {
				const valueToStore = value instanceof Function ? value(prev) : value;
				window.localStorage.setItem(key, JSON.stringify(valueToStore));
				return valueToStore;
			});
		},
		[key]
	);

	return [storedValue, setValue] as const;
}

// useDebounce - Debounced value
function useDebounce<T>(value: T, delay: number): T {
	const [debouncedValue, setDebouncedValue] = useState(value);

	useEffect(() => {
		const handler = setTimeout(() => setDebouncedValue(value), delay);
		return () => clearTimeout(handler);
	}, [value, delay]);

	return debouncedValue;
}

// useAsync - Async operation state
function useAsync<T>(asyncFunction: () => Promise<T>, deps: unknown[] = []) {
	const [state, setState] = useState<{
		data: T | null;
		loading: boolean;
		error: Error | null;
	}>({
		data: null,
		loading: true,
		error: null,
	});

	useEffect(() => {
		let mounted = true;
		setState((s) => ({ ...s, loading: true }));

		asyncFunction()
			.then((data) => mounted && setState({ data, loading: false, error: null }))
			.catch((error) => mounted && setState({ data: null, loading: false, error }));

		return () => {
			mounted = false;
		};
	}, deps);

	return state;
}
```

---

## State Management

### 1. useReducer for Complex State

```tsx
type State = {
	items: Item[];
	loading: boolean;
	error: string | null;
};

type Action =
	| { type: 'FETCH_START' }
	| { type: 'FETCH_SUCCESS'; payload: Item[] }
	| { type: 'FETCH_ERROR'; payload: string }
	| { type: 'ADD_ITEM'; payload: Item }
	| { type: 'REMOVE_ITEM'; payload: string };

function reducer(state: State, action: Action): State {
	switch (action.type) {
		case 'FETCH_START':
			return { ...state, loading: true, error: null };
		case 'FETCH_SUCCESS':
			return { ...state, loading: false, items: action.payload };
		case 'FETCH_ERROR':
			return { ...state, loading: false, error: action.payload };
		case 'ADD_ITEM':
			return { ...state, items: [...state.items, action.payload] };
		case 'REMOVE_ITEM':
			return { ...state, items: state.items.filter((i) => i.id !== action.payload) };
		default:
			return state;
	}
}
```

### 2. Context for Global State

```tsx
interface AppContextValue {
	user: User | null;
	theme: 'light' | 'dark';
	setTheme: (theme: 'light' | 'dark') => void;
}

const AppContext = createContext<AppContextValue | undefined>(undefined);

export function AppProvider({ children }: { children: ReactNode }) {
	const [theme, setTheme] = useState<'light' | 'dark'>('light');
	const { data: user } = useUser();

	const value = useMemo(() => ({ user, theme, setTheme }), [user, theme]);

	return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
}

export function useApp() {
	const context = useContext(AppContext);
	if (!context) throw new Error('useApp must be used within AppProvider');
	return context;
}
```

---

## Performance Optimization

### 1. Memoization

```tsx
// memo - Prevent re-renders
const ExpensiveList = memo(function ExpensiveList({ items }: { items: Item[] }) {
	return (
		<ul>
			{items.map((item) => (
				<li key={item.id}>{item.name}</li>
			))}
		</ul>
	);
});

// useMemo - Expensive calculations
function Dashboard({ data }: { data: DataPoint[] }) {
	const processedData = useMemo(() => {
		return data.map((d) => ({ ...d, computed: expensiveCalculation(d) }));
	}, [data]);

	return <Chart data={processedData} />;
}

// useCallback - Stable function references
function Parent() {
	const [count, setCount] = useState(0);

	const handleClick = useCallback(() => {
		setCount((c) => c + 1);
	}, []);

	return <Child onClick={handleClick} />;
}
```

### 2. Code Splitting

```tsx
import { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
	return (
		<Suspense fallback={<Loading />}>
			<HeavyComponent />
		</Suspense>
	);
}
```

---

## TypeScript Patterns

### Props Types

```tsx
// Discriminated unions for variants
type ButtonProps = { variant: 'primary'; onClick: () => void } | { variant: 'link'; href: string };

function Button(props: ButtonProps) {
	if (props.variant === 'link') {
		return <a href={props.href}>Link</a>;
	}
	return <button onClick={props.onClick}>Button</button>;
}

// Generic components
interface ListProps<T> {
	items: T[];
	renderItem: (item: T) => ReactNode;
	keyExtractor: (item: T) => string;
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
	return (
		<ul>
			{items.map((item) => (
				<li key={keyExtractor(item)}>{renderItem(item)}</li>
			))}
		</ul>
	);
}
```

---

## Agent Integration

This skill is used by:

- **react-expert** subagent
- **render-optimizer** for performance issues
- **ui-mobile/tablet/desktop** for component patterns

---

## FORBIDDEN

1. **Class components** - Use function components
2. **Prop drilling** - Use context or composition
3. **Inline objects/functions in JSX** - Causes re-renders
4. **useEffect for derived state** - Use useMemo
5. **Mutating state directly** - Always use setState/dispatch

---

## Version

- **v1.0.0** - Initial implementation based on React 19 patterns

