21 React Hooks
21.1 Basic
21.1.1 What Are Hooks?
React Hooks are special functions that let you “hook into” React features like state and lifecycle methods in functional components. They were introduced in React 16.8 (2019) to solve a major limitation.
21.1.1.1 The Problem They Solved
Think of this analogy with Flutter:
Flutter (before Hooks equivalent):
┌─────────────────────────────┐
│ StatelessWidget │ → No state, simple
│ (like functional component) │
└─────────────────────────────┘
┌─────────────────────────────┐
│ StatefulWidget │ → Has state, lifecycle
│ (like class component) │ methods (initState,
└─────────────────────────────┘ dispose, etc.)
In React before Hooks:
┌──────────────────────────────┐
│ Function Component │ → Simple, no state ❌
│ const MyComponent = () => {} │
└──────────────────────────────┘
┌──────────────────────────────┐
│ Class Component │ → Has state, lifecycle ✓
│ class MyComponent extends │ But verbose, complex
│ React.Component {} │
└──────────────────────────────┘
Hooks let you use state and other React features in function components!
21.1.2 Core Hooks (The Essential Ones)
21.1.2.1 1. useState - Managing State
Similar to Flutter’s setState()
:
import { useState } from 'react';
function Counter() {
// Declare state variable
const [count, setCount] = useState(0);
// ↑ ↑ ↑
// value setter initial value
return (
<button onClick={() => setCount(count + 1)}>
: {count}
Count</button>
;
) }
Flutter equivalent:
class Counter extends StatefulWidget {
@override
=> _CounterState();
_CounterState createState() }
class _CounterState extends State<Counter> {
int count = 0; // State variable
@override
{
Widget build(BuildContext context) return TextButton(
: () => setState(() { count++; }),
onPressed: Text('Count: $count'),
child
);}
}
21.1.2.2 2. useEffect - Side Effects & Lifecycle
Similar to Flutter’s initState
, dispose
, and didUpdateWidget
:
import { useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
// Runs after render (like initState)
fetchData().then(result => setData(result));
// Cleanup function (like dispose)
return () => {
cancelFetch();
;
}, []); // Empty array = run once on mount
}// ↑ Dependencies array
}
Dependency array controls when effect runs:
useEffect(() => { ... }, []) → Run once (mount only)
useEffect(() => { ... }, [a, b]) → Run when a or b changes
useEffect(() => { ... }) → Run after every render
Flutter lifecycle equivalent:
┌─────────────────────────────────────────┐
│ React useEffect │
├─────────────────────────────────────────┤
│ useEffect(() => { │
│ // mount logic │ ≈ initState()
│ return () => { /* cleanup */ } │ ≈ dispose()
│ }, [dependency]) │ ≈ didUpdateWidget()
└─────────────────────────────────────────┘
21.1.3 Why Hooks Are Great
Coming from Flutter, you’ll appreciate:
- Less boilerplate - No need for separate State classes
- Reusable logic - Create custom hooks (like extracting methods)
- Composition - Combine multiple hooks easily
- Better than class lifecycle - More intuitive than componentDidMount, componentDidUpdate, etc.
21.1.4 Key Rules
- Only call hooks at the top level (not in loops/conditions)
- Only call hooks in React functions (components or custom hooks)