React Essentials
Quick reference guide for React - a JavaScript library for building user interfaces.
What is React?
React is a JavaScript library developed by Facebook for building UIs:
Installation
Create React App
# ========== Create New Project ==========
npx create-react-app my-app
# With TypeScript
npx create-react-app my-app --template typescript
# ========== Start Development Server ==========
cd my-app
npm start
# Open http://localhost:3000
Vite (Recommended)
# ========== Create New Project ==========
npm create vite@latest my-app -- --template react
# With TypeScript
npm create vite@latest my-app -- --template react-ts
# ========== Install and Run ==========
cd my-app
npm install
npm run dev
Manual Setup
# ========== Install React ==========
npm install react react-dom
# For TypeScript
npm install --save-dev @types/react @types/react-dom
Components
Function Components
// ========== Basic Function Component ==========
function Welcome() {
return <h1>Hello, World!</h1>;
}
// ========== With Props ==========
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
// Usage
<Welcome name="Alice" />
// ========== With Destructuring ==========
function Welcome({ name, age }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>Age: {age}</p>
</div>
);
}
// ========== TypeScript ==========
interface WelcomeProps {
name: string;
age: number;
}
function Welcome({ name, age }: WelcomeProps) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>Age: {age}</p>
</div>
);
}
// ========== With Default Props ==========
function Welcome({ name = 'Guest', age = 0 }) {
return <h1>Hello, {name}!</h1>;
}
// ========== Arrow Function ==========
const Welcome = ({ name }) => {
return <h1>Hello, {name}!</h1>;
};
// Short form
const Welcome = ({ name }) => <h1>Hello, {name}!</h1>;
Class Components (Legacy)
// ========== Basic Class Component ==========
import React, { Component } from 'react';
class Welcome extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
// ========== With State ==========
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
// ========== Lifecycle Methods ==========
class UserProfile extends Component {
componentDidMount() {
// Called after component is mounted
console.log('Component mounted');
}
componentDidUpdate(prevProps, prevState) {
// Called after component updates
console.log('Component updated');
}
componentWillUnmount() {
// Called before component is unmounted
console.log('Component will unmount');
}
render() {
return <div>Profile</div>;
}
}
JSX
Basic Syntax
// ========== JavaScript Expressions ==========
const name = 'Alice';
const element = <h1>Hello, {name}!</h1>;
const user = { name: 'Bob', age: 30 };
const element = <h1>Hello, {user.name}!</h1>;
// ========== Attributes ==========
const element = <img src={imageUrl} alt="Description" />;
const element = <div className="container">Content</div>;
const element = <button onClick={handleClick}>Click me</button>;
// ========== Inline Styles ==========
const element = (
<div style={{ color: 'red', fontSize: '20px' }}>
Styled text
</div>
);
// ========== Children ==========
const element = (
<div>
<h1>Title</h1>
<p>Paragraph</p>
</div>
);
// ========== Conditional Rendering ==========
const element = (
<div>
{isLoggedIn ? <UserGreeting /> : <GuestGreeting />}
</div>
);
// Short circuit
const element = (
<div>
{isLoggedIn && <UserGreeting />}
</div>
);
// ========== Lists ==========
const items = ['Apple', 'Banana', 'Orange'];
const element = (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
// ========== Fragments ==========
const element = (
<>
<h1>Title</h1>
<p>Paragraph</p>
</>
);
// Or
const element = (
<React.Fragment>
<h1>Title</h1>
<p>Paragraph</p>
</React.Fragment>
);
Hooks
useState
// ========== Basic State ==========
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(count - 1)}>Decrement</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
// ========== Multiple State Variables ==========
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
return (
<form>
<input value={name} onChange={(e) => setName(e.target.value)} />
<input value={email} onChange={(e) => setEmail(e.target.value)} />
<input value={age} onChange={(e) => setAge(Number(e.target.value))} />
</form>
);
}
// ========== Object State ==========
function UserProfile() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
const updateName = (name) => {
setUser({ ...user, name });
};
const updateEmail = (email) => {
setUser((prevUser) => ({ ...prevUser, email }));
};
return <div>{user.name}</div>;
}
// ========== Array State ==========
function TodoList() {
const [todos, setTodos] = useState([]);
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text }]);
};
const removeTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
const updateTodo = (id, newText) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, text: newText } : todo
));
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
// ========== Lazy Initialization ==========
function ExpensiveComponent() {
const [data, setData] = useState(() => {
// Only runs on initial render
return computeExpensiveValue();
});
return <div>{data}</div>;
}
useEffect
// ========== Basic Effect ==========
import { useEffect } from 'react';
function Example() {
useEffect(() => {
// Runs after every render
console.log('Component rendered');
});
return <div>Example</div>;
}
// ========== Effect with Dependencies ==========
function User({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// Runs when userId changes
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
return <div>{user?.name}</div>;
}
// ========== Effect with Cleanup ==========
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1);
}, 1000);
// Cleanup function
return () => clearInterval(interval);
}, []);
return <div>Count: {count}</div>;
}
// ========== Run Once (Component Mount) ==========
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
// Only runs once on mount
fetchData().then(setData);
}, []);
return <div>{data}</div>;
}
// ========== Multiple Effects ==========
function Component() {
useEffect(() => {
// Effect 1: Data fetching
fetchData();
}, []);
useEffect(() => {
// Effect 2: Event listener
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
useEffect(() => {
// Effect 3: Document title
document.title = `Count: ${count}`;
}, [count]);
return <div>Component</div>;
}
useContext
// ========== Create Context ==========
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
// ========== Provider Component ==========
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// ========== Consumer Component ==========
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
style={{
background: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#333' : '#fff'
}}
>
Toggle Theme
</button>
);
}
// ========== App ==========
function App() {
return (
<ThemeProvider>
<ThemedButton />
</ThemeProvider>
);
}
// ========== Auth Context Example ==========
const AuthContext = createContext();
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = async (email, password) => {
const user = await api.login(email, password);
setUser(user);
};
const logout = () => {
setUser(null);
};
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
useReducer
// ========== Basic Reducer ==========
import { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
throw new Error('Unknown action');
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}
// ========== Complex State Management ==========
const initialState = {
todos: [],
filter: 'all'
};
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
};
case 'REMOVE_TODO':
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
};
case 'SET_FILTER':
return {
...state,
filter: action.payload
};
default:
return state;
}
}
function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, initialState);
const addTodo = (text) => {
dispatch({
type: 'ADD_TODO',
payload: { id: Date.now(), text, completed: false }
});
};
return <div>Todo App</div>;
}
useRef
// ========== DOM Reference ==========
import { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef(null);
useEffect(() => {
// Focus input on mount
inputRef.current.focus();
}, []);
return <input ref={inputRef} />;
}
// ========== Previous Value ==========
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
}, [count]);
const prevCount = prevCountRef.current;
return (
<div>
<p>Current: {count}</p>
<p>Previous: {prevCount}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
// ========== Mutable Value ==========
function Timer() {
const [count, setCount] = useState(0);
const intervalRef = useRef();
const start = () => {
if (intervalRef.current) return;
intervalRef.current = setInterval(() => {
setCount(c => c + 1);
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
intervalRef.current = null;
};
return (
<div>
<p>Count: {count}</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
);
}
useMemo
// ========== Expensive Computation ==========
import { useMemo } from 'react';
function ExpensiveComponent({ items }) {
const sortedItems = useMemo(() => {
console.log('Sorting items...');
return items.sort((a, b) => a.value - b.value);
}, [items]);
return (
<ul>
{sortedItems.map(item => (
<li key={item.id}>{item.value}</li>
))}
</ul>
);
}
// ========== Derived State ==========
function UserList({ users, searchQuery }) {
const filteredUsers = useMemo(() => {
return users.filter(user =>
user.name.toLowerCase().includes(searchQuery.toLowerCase())
);
}, [users, searchQuery]);
return (
<ul>
{filteredUsers.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
useCallback
// ========== Prevent Unnecessary Re-renders ==========
import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// Without useCallback, new function on every render
const handleClick = useCallback(() => {
console.log('Button clicked');
}, []);
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<Child onClick={handleClick} />
</div>
);
}
const Child = React.memo(({ onClick }) => {
console.log('Child rendered');
return <button onClick={onClick}>Click me</button>;
});
// ========== With Dependencies ==========
function SearchComponent() {
const [query, setQuery] = useState('');
const handleSearch = useCallback((value) => {
console.log('Searching for:', value);
api.search(query, value);
}, [query]);
return <SearchInput onSearch={handleSearch} />;
}
Custom Hooks
// ========== useFetch Hook ==========
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
})
.catch(err => {
setError(err);
setLoading(false);
});
}, [url]);
return { data, loading, error };
}
// Usage
function Users() {
const { data, loading, error } = useFetch('/api/users');
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// ========== useLocalStorage Hook ==========
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
// Usage
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Current theme: {theme}
</button>
);
}
// ========== useDebounce Hook ==========
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(handler);
}, [value, delay]);
return debouncedValue;
}
// Usage
function SearchBox() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 500);
useEffect(() => {
if (debouncedQuery) {
api.search(debouncedQuery);
}
}, [debouncedQuery]);
return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
}
Forms
Controlled Components
// ========== Simple Form ==========
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
console.log('Login:', { email, password });
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
<button type="submit">Login</button>
</form>
);
}
// ========== Complex Form ==========
function RegistrationForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
password: '',
country: '',
terms: false
});
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('Form data:', formData);
};
return (
<form onSubmit={handleSubmit}>
<input
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
/>
<input
name="email"
type="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
<input
name="password"
type="password"
value={formData.password}
onChange={handleChange}
placeholder="Password"
/>
<select name="country" value={formData.country} onChange={handleChange}>
<option value="">Select country</option>
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
</select>
<label>
<input
name="terms"
type="checkbox"
checked={formData.terms}
onChange={handleChange}
/>
Accept terms
</label>
<button type="submit">Register</button>
</form>
);
}
Form Validation
// ========== With Validation ==========
function ValidatedForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errors, setErrors] = useState({});
const validate = () => {
const newErrors = {};
if (!email) {
newErrors.email = 'Email is required';
} else if (!/\S+@\S+\.\S+/.test(email)) {
newErrors.email = 'Email is invalid';
}
if (!password) {
newErrors.password = 'Password is required';
} else if (password.length < 8) {
newErrors.password = 'Password must be at least 8 characters';
}
return newErrors;
};
const handleSubmit = (e) => {
e.preventDefault();
const validationErrors = validate();
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
return;
}
// Submit form
console.log('Form submitted');
setErrors({});
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>
<div>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
{errors.password && <span className="error">{errors.password}</span>}
</div>
<button type="submit">Submit</button>
</form>
);
}
Performance Optimization
React.memo
// ========== Prevent Re-renders ==========
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
console.log('Rendering ExpensiveComponent');
return <div>{data}</div>;
});
// ========== With Custom Comparison ==========
const UserCard = React.memo(
function UserCard({ user }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// Return true if props are equal (skip re-render)
return prevProps.user.id === nextProps.user.id;
}
);
Code Splitting
// ========== Lazy Loading ==========
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
// ========== Route-based Splitting ==========
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</Suspense>
);
}
Error Handling
Error Boundaries
// ========== Error Boundary Component ==========
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught:', error, errorInfo);
// Log to error reporting service
}
render() {
if (this.state.hasError) {
return (
<div>
<h1>Something went wrong</h1>
<p>{this.state.error?.message}</p>
</div>
);
}
return this.props.children;
}
}
// Usage
function App() {
return (
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
);
}
Common Patterns
Container/Presentational Pattern
// ========== Presentational Component ==========
function UserList({ users, onUserClick }) {
return (
<ul>
{users.map(user => (
<li key={user.id} onClick={() => onUserClick(user)}>
{user.name}
</li>
))}
</ul>
);
}
// ========== Container Component ==========
function UserListContainer() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUsers().then(data => {
setUsers(data);
setLoading(false);
});
}, []);
const handleUserClick = (user) => {
console.log('User clicked:', user);
};
if (loading) return <div>Loading...</div>;
return <UserList users={users} onUserClick={handleUserClick} />;
}
Render Props
// ========== Render Prop Component ==========
function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handleMouseMove = (e) => {
setPosition({ x: e.clientX, y: e.clientY });
};
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, []);
return render(position);
}
// Usage
function App() {
return (
<MouseTracker
render={({ x, y }) => (
<div>Mouse position: {x}, {y}</div>
)}
/>
);
}
Higher-Order Components (HOC)
// ========== HOC ==========
function withLoading(Component) {
return function WithLoadingComponent({ isLoading, ...props }) {
if (isLoading) {
return <div>Loading...</div>;
}
return <Component {...props} />;
};
}
// Usage
const UserListWithLoading = withLoading(UserList);
function App() {
const [isLoading, setIsLoading] = useState(true);
const [users, setUsers] = useState([]);
return <UserListWithLoading isLoading={isLoading} users={users} />;
}
Best Practices
TypeScript with React
// ========== Component Props ==========
interface ButtonProps {
text: string;
onClick: () => void;
disabled?: boolean;
}
function Button({ text, onClick, disabled = false }: ButtonProps) {
return (
<button onClick={onClick} disabled={disabled}>
{text}
</button>
);
}
// ========== With Children ==========
interface CardProps {
title: string;
children: React.ReactNode;
}
function Card({ title, children }: CardProps) {
return (
<div>
<h2>{title}</h2>
{children}
</div>
);
}
// ========== Event Handlers ==========
function Form() {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
</form>
);
}
// ========== Generic Components ==========
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{renderItem(item)}</li>
))}
</ul>
);
}