What Is React?
React is a JavaScript library developed by Facebook (Meta) for building user interfaces. It uses a component-based architecture where the UI is broken into independent, reusable pieces that each manage their own logic and appearance. React uses a virtual DOM to efficiently update only the parts of the real DOM that have changed, making it fast even for complex UIs.
JSX: JavaScript + HTML
JSX is a syntax extension that looks like HTML but compiles to JavaScript function calls. Every JSX expression must have a single root element:
// Valid JSX
const element = (
<div className="container">
<h1>Hello, {name}!</h1>
<p>Welcome to React.</p>
</div>
);
// Use fragments to avoid extra DOM nodes
const element = (
<>
<h1>Title</h1>
<p>Paragraph</p>
</>
);
Key JSX differences from HTML: | HTML | JSX | |---|---| | class="..." | className="..." | | for="..." | htmlFor="..." | | <input /> self-closing | Same, but required | | onclick="..." | onClick={handler} | | Inline styles as strings | Inline styles as objects |
// Inline styles in JSX
<div style={{ backgroundColor: 'blue', fontSize: '16px' }}>
Styled element
</div>
Functional Components
The modern way to write React components:
// Basic functional component
function Greeting({ name, age }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>You are {age} years old.</p>
</div>
);
}
// Arrow function syntax
const Greeting = ({ name, age }) => (
<div>
<h1>Hello, {name}!</h1>
<p>You are {age} years old.</p>
</div>
);
// Usage
<Greeting name="Alice" age={30} />
Props
Props are read-only data passed from parent to child components:
// Passing various prop types
<UserCard
name="Alice" // String
age={30} // Number (use curly braces)
isActive={true} // Boolean
hobbies={['reading', 'coding']} // Array
address={{ city: 'NYC' }} // Object
onClick={handleClick} // Function
/>
// Default props
function Button({ label = 'Click me', variant = 'primary' }) {
return <button className={variant}>{label}</button>;
}
// Spreading props
const buttonProps = { label: 'Submit', disabled: false };
<Button {...buttonProps} />
// Children prop
function Card({ children, title }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-body">{children}</div>
</div>
);
}
// Usage with children
<Card title="My Card">
<p>This is the card content.</p>
</Card>
useState Hook
Manages local component state:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(c => c - 1)}>-</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
// Object state
const [user, setUser] = useState({ name: '', email: '' });
// Always spread to avoid overwriting other fields
setUser(prev => ({ ...prev, name: 'Alice' }));
// Array state
const [items, setItems] = useState([]);
setItems(prev => [...prev, newItem]); // Add
setItems(prev => prev.filter(i => i.id !== id)); // Remove
setItems(prev => prev.map(i => i.id === id ? {...i, ...changes} : i)); // Update
useEffect Hook
Handles side effects (data fetching, subscriptions, DOM manipulation):
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Runs after every render (no dependency array)
// document.title = 'Hello';
});
useEffect(() => {
// Runs once on mount (empty dependency array)
console.log('Component mounted');
return () => console.log('Component unmounted'); // Cleanup
}, []);
useEffect(() => {
// Runs when userId changes
setLoading(true);
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
});
}, [userId]); // Dependency array
if (loading) return <p>Loading...</p>;
return <div>{user?.name}</div>;
}
Event Handling
function Form() {
const [value, setValue] = useState('');
// Synthetic events work just like native DOM events
const handleChange = (e) => setValue(e.target.value);
const handleSubmit = (e) => {
e.preventDefault(); // Prevent page reload
console.log('Submitted:', value);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={value}
onChange={handleChange}
onFocus={() => console.log('Focused')}
onBlur={() => console.log('Blurred')}
/>
<button type="submit">Submit</button>
</form>
);
}
Conditional Rendering
function Status({ isLoggedIn, role }) {
// Ternary operator
return <div>{isLoggedIn ? <UserMenu /> : <LoginButton />}</div>;
}
// Short-circuit evaluation (render nothing if false)
{isLoggedIn && <UserMenu />}
// Null to render nothing
if (!data) return null;
// Multiple conditions
function Badge({ role }) {
if (role === 'admin') return <span className="admin">Admin</span>;
if (role === 'mod') return <span className="mod">Moderator</span>;
return <span>User</span>;
}
Lists and Keys
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
// Key must be unique among siblings, stable, and not index if list reorders
<li key={todo.id} className={todo.done ? 'done' : ''}>
{todo.text}
</li>
))}
</ul>
);
}
useContext: Avoiding Prop Drilling
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext('light');
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Toolbar />
</ThemeContext.Provider>
);
}
function ThemedButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
style={{ background: theme === 'dark' ? '#333' : '#fff' }}
onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}
>
Toggle Theme
</button>
);
}
useRef Hook
import { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // Focus on mount
}, []);
// Store mutable value without causing re-render
const renderCount = useRef(0);
renderCount.current += 1;
return <input ref={inputRef} />;
}
Custom Hooks
Extract reusable stateful logic into custom hooks (functions starting with use):
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(err => { if (err.name !== 'AbortError') setError(err); })
.finally(() => setLoading(false));
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
// Usage
const { data: users, loading } = useFetch('/api/users');
Quick Reference Summary
| Hook | Purpose |
|---|---|
| useState | Local component state |
| useEffect | Side effects (fetch, subscriptions) |
| useContext | Consume context without prop drilling |
| useRef | DOM references, mutable values |
| useMemo | Memoize expensive computations |
| useCallback | Memoize function references |
| useReducer | Complex state logic |