| name | react |
| description | Builds user interfaces with React using components, hooks, state management, and modern patterns. Use when creating React components, managing state, handling side effects, optimizing performance, or integrating with frameworks. |
React
Library for building user interfaces with components, declarative rendering, and hooks-based state management.
Quick Start
Create with Vite (recommended):
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev
Or with a meta-framework:
npx create-next-app@latest # Next.js
npx create-remix@latest # Remix
Components
Function Components
// Basic component
function Greeting({ name }: { name: string }) {
return <h1>Hello, {name}!</h1>;
}
// Arrow function with destructured props
const Button = ({ onClick, children }: {
onClick: () => void;
children: React.ReactNode;
}) => {
return <button onClick={onClick}>{children}</button>;
};
// Usage
<Greeting name="World" />
<Button onClick={() => alert('Clicked!')}>Click me</Button>
Props Interface Pattern
interface CardProps {
title: string;
description?: string;
children: React.ReactNode;
variant?: 'default' | 'outlined';
}
function Card({ title, description, children, variant = 'default' }: CardProps) {
return (
<div className={`card card--${variant}`}>
<h2>{title}</h2>
{description && <p>{description}</p>}
{children}
</div>
);
}
JSX
Expressions
const name = 'React';
const items = ['Apple', 'Banana', 'Cherry'];
function App() {
return (
<div>
{/* Variables */}
<h1>{name}</h1>
{/* Expressions */}
<p>{2 + 2}</p>
<p>{name.toUpperCase()}</p>
{/* Conditional rendering */}
{name && <p>Hello, {name}</p>}
{items.length > 0 ? <List items={items} /> : <p>No items</p>}
{/* Lists */}
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
Fragments
// Named fragment
import { Fragment } from 'react';
function List() {
return (
<Fragment>
<li>Item 1</li>
<li>Item 2</li>
</Fragment>
);
}
// Shorthand syntax
function List() {
return (
<>
<li>Item 1</li>
<li>Item 2</li>
</>
);
}
Hooks
useState
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount((c) => c - 1)}>Decrement</button>
</div>
);
}
// With objects
const [user, setUser] = useState({ name: '', email: '' });
setUser((prev) => ({ ...prev, name: 'John' }));
// Lazy initialization
const [data, setData] = useState(() => expensiveComputation());
useEffect
import { useEffect, useState } from 'react';
function DataFetcher({ url }: { url: string }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
async function fetchData() {
setLoading(true);
const response = await fetch(url);
const json = await response.json();
if (!cancelled) {
setData(json);
setLoading(false);
}
}
fetchData();
// Cleanup function
return () => {
cancelled = true;
};
}, [url]); // Dependency array
if (loading) return <p>Loading...</p>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
useRef
import { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// Focus on mount
inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
// Mutable ref (doesn't trigger re-render)
function Timer() {
const countRef = useRef(0);
useEffect(() => {
const id = setInterval(() => {
countRef.current += 1;
}, 1000);
return () => clearInterval(id);
}, []);
}
useMemo and useCallback
import { useMemo, useCallback, useState } from 'react';
function ExpensiveList({ items, filter }: { items: Item[]; filter: string }) {
// Memoize expensive computation
const filteredItems = useMemo(() => {
return items.filter((item) =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}, [items, filter]);
// Memoize callback
const handleClick = useCallback((id: string) => {
console.log('Clicked:', id);
}, []);
return (
<ul>
{filteredItems.map((item) => (
<ListItem key={item.id} item={item} onClick={handleClick} />
))}
</ul>
);
}
useReducer
import { useReducer } from 'react';
interface State {
count: number;
error: string | null;
}
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'reset'; payload: number }
| { type: 'error'; payload: string };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'reset':
return { ...state, count: action.payload };
case 'error':
return { ...state, error: action.payload };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0, error: null });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset', payload: 0 })}>Reset</button>
</div>
);
}
useContext
import { createContext, useContext, useState } from 'react';
interface ThemeContextType {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | null>(null);
function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const toggleTheme = () => {
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Usage
function ThemedButton() {
const { theme, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme}>
Current theme: {theme}
</button>
);
}
Custom Hooks
// useFetch
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const controller = new AbortController();
async function fetchData() {
try {
setLoading(true);
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error('Failed to fetch');
const json = await response.json();
setData(json);
} catch (err) {
if (err instanceof Error && err.name !== 'AbortError') {
setError(err);
}
} finally {
setLoading(false);
}
}
fetchData();
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
// useLocalStorage
function useLocalStorage<T>(key: string, initialValue: T) {
const [value, setValue] = useState<T>(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue] as const;
}
// useDebounce
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
Event Handling
function Form() {
const [value, setValue] = useState('');
// Input change
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
// Form submit
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log('Submitted:', value);
};
// Button click
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log('Clicked at:', e.clientX, e.clientY);
};
// Keyboard
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
console.log('Enter pressed');
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={value}
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
<button type="submit" onClick={handleClick}>
Submit
</button>
</form>
);
}
Composition Patterns
Children
function Container({ children }: { children: React.ReactNode }) {
return <div className="container">{children}</div>;
}
// Usage
<Container>
<h1>Title</h1>
<p>Content</p>
</Container>
Render Props
interface MousePosition {
x: number;
y: number;
}
function MouseTracker({
render,
}: {
render: (position: MousePosition) => React.ReactNode;
}) {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (e: React.MouseEvent) => {
setPosition({ x: e.clientX, y: e.clientY });
};
return <div onMouseMove={handleMouseMove}>{render(position)}</div>;
}
// Usage
<MouseTracker
render={({ x, y }) => (
<p>
Mouse position: {x}, {y}
</p>
)}
/>
Compound Components
const TabsContext = createContext<{
activeTab: string;
setActiveTab: (tab: string) => void;
} | null>(null);
function Tabs({ children, defaultTab }: {
children: React.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: React.ReactNode }) {
return <div className="tab-list">{children}</div>;
}
function Tab({ id, children }: { id: string; children: React.ReactNode }) {
const context = useContext(TabsContext);
if (!context) throw new Error('Tab must be used within Tabs');
return (
<button
className={context.activeTab === id ? 'active' : ''}
onClick={() => context.setActiveTab(id)}
>
{children}
</button>
);
}
function TabPanels({ children }: { children: React.ReactNode }) {
return <div className="tab-panels">{children}</div>;
}
function TabPanel({ id, children }: { id: string; children: React.ReactNode }) {
const context = useContext(TabsContext);
if (!context) throw new Error('TabPanel must be used within Tabs');
return context.activeTab === id ? <div>{children}</div> : null;
}
// Usage
<Tabs defaultTab="tab1">
<TabList>
<Tab id="tab1">Tab 1</Tab>
<Tab id="tab2">Tab 2</Tab>
</TabList>
<TabPanels>
<TabPanel id="tab1">Content 1</TabPanel>
<TabPanel id="tab2">Content 2</TabPanel>
</TabPanels>
</Tabs>
Performance
React.memo
import { memo } from 'react';
interface ItemProps {
item: { id: string; name: string };
onClick: (id: string) => void;
}
const ListItem = memo(function ListItem({ item, onClick }: ItemProps) {
console.log('Rendering:', item.name);
return <li onClick={() => onClick(item.id)}>{item.name}</li>;
});
// With custom comparison
const ListItem = memo(
function ListItem({ item, onClick }: ItemProps) {
return <li onClick={() => onClick(item.id)}>{item.name}</li>;
},
(prevProps, nextProps) => {
return prevProps.item.id === nextProps.item.id;
}
);
Lazy Loading
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
Error Boundaries
import { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Error caught:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback || <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Usage
<ErrorBoundary fallback={<ErrorPage />}>
<MyComponent />
</ErrorBoundary>
Best Practices
- Keep components small - Single responsibility principle
- Lift state up - Share state via common ancestor
- Use TypeScript - Catch errors at compile time
- Memoize expensive operations - useMemo, useCallback, memo
- Avoid inline objects/functions in JSX - They cause re-renders
- Use keys correctly - Unique, stable identifiers
Common Mistakes
| Mistake | Fix |
|---|---|
| Mutating state directly | Use spread operator or setState |
| Missing dependency in useEffect | Add all dependencies |
| Unnecessary useEffect | Derive state during render |
| Prop drilling | Use Context or state management |
| Not cleaning up effects | Return cleanup function |
Reference Files
- references/hooks.md - Hooks deep dive
- references/patterns.md - Advanced patterns
- references/performance.md - Optimization techniques