Function components are the standard way to write React in 2024 and beyond. Combined with hooks, they replace class components for state, lifecycle, and side effects. This lesson explains why and how to write them effectively.
Why Function Components Won
Before hooks (React 16.8), only class components could hold state and lifecycle methods. Hooks brought that capability to plain functions, resulting in less boilerplate, easier testing, and better TypeScript inference.
The React team no longer recommends class components for new code. Libraries, docs, and tooling assume function components and hooks. You may still encounter classes in legacy codebases.
State and lifecycle in one concise function — no this, no constructor, no binding.
Function Component Anatomy
function MyComponent({ propA, propB = 'default' }) {
// 1. Hooks (always at top level)
const [state, setState] = useState(initial);
// 2. Event handlers and helpers
const handleClick = () => setState(x => x + 1);
// 3. Return JSX
return <button onClick={handleClick}>{state}</button>;
}
Destructure props in the parameter list for clarity.
Call hooks only at the top level — never inside conditions or loops.
Return JSX (or null to render nothing).
Use arrow functions or function declarations — both are valid.
Function vs Class Components
How modern function components map to legacy class patterns.
Class (legacy)
Function + Hooks (modern)
this.state
useState()
componentDidMount
useEffect(() => {}, [])
componentDidUpdate
useEffect(() => {}, [dep])
componentWillUnmount
useEffect cleanup return
this.setState
setState from useState
Context Consumer
useContext()
ref callback
useRef()
Arrow Functions vs Function Declarations
Both function Button() {} and const Button = () => {} work for components. Function declarations are hoisted; arrow components are not. Teams often prefer function for top-level components and arrows for inline callbacks.
When You Still See Class Components
Legacy apps, older tutorials, and some error boundaries (only classes could implement componentDidCatch until recently) may use classes. React 19 adds function component error boundaries in some setups, but many codebases still use class error boundaries.
Maintaining existing class-based code without full rewrites.
Some third-party libraries document class-based examples.
Error boundaries historically required classes (check your React version).
New features should always use function components.
Common Mistakes
Mixing class lifecycle mental models with hooks incorrectly.
Using this in function components — it does not refer to the component instance.
Converting classes to functions line-by-line instead of rethinking state with hooks.
Calling hooks conditionally inside function components.
Key Takeaways
Function components with hooks are the modern React standard.
Hooks replace state, lifecycle, context, and refs from class components.
Less boilerplate and better composability than classes.
Use function components for all new React code.
Pro Tip
When reading legacy class code, map each lifecycle method to the equivalent useEffect pattern before rewriting.
You can write modern function components confidently. Next, learn how props pass data between them.