React Internals
React Hooks
Use state and lifecycle features in functional components
Intermediate
react hooks state functional-components
Definition
React Hooks are functions that let you use state and other React features in functional components. Introduced in React 16.8, they enable you to reuse stateful logic without changing your component hierarchy and make components more composable.
Core 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(prev => prev - 1)}>
Decrement (functional update)
</button>
</div>
);
}
useEffect
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Runs after render
let cancelled = false;
async function fetchUser() {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
if (!cancelled) {
setUser(data);
setLoading(false);
}
}
fetchUser();
// Cleanup function
return () => {
cancelled = true;
};
}, [userId]); // Dependency array
if (loading) return <div>Loading...</div>;
return <div>{user.name}</div>;
}
useContext
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext(null);
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemedButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
className={theme}
>
Current theme: {theme}
</button>
);
}
Additional Hooks
useRef
import { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef(null);
useEffect(() => {
// Focus input on mount
inputRef.current.focus();
}, []);
// Store previous value
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
});
return <input ref={inputRef} />;
}
useMemo and useCallback
import { useMemo, useCallback } from 'react';
function ExpensiveComponent({ data, onUpdate }) {
// Memoize expensive computation
const processedData = useMemo(() => {
return data.map(item => expensiveOperation(item));
}, [data]);
// Memoize callback
const handleClick = useCallback((id) => {
onUpdate(id);
}, [onUpdate]);
return (
<List
items={processedData}
onItemClick={handleClick}
/>
);
}
useReducer
import { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return initialState;
default:
throw new Error('Unknown action');
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}
Custom Hooks
useLocalStorage
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
const setValue = (value) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
}
// Usage
const [name, setName] = useLocalStorage('name', 'Guest');
useFetch
function useFetch(url) {
const [state, setState] = useState({
data: null,
loading: true,
error: null
});
useEffect(() => {
let cancelled = false;
async function fetchData() {
try {
setState(prev => ({ ...prev, loading: true }));
const response = await fetch(url);
const data = await response.json();
if (!cancelled) {
setState({ data, loading: false, error: null });
}
} catch (error) {
if (!cancelled) {
setState({ data: null, loading: false, error });
}
}
}
fetchData();
return () => {
cancelled = true;
};
}, [url]);
return state;
}
Rules of Hooks
- Only call hooks at the top level - not in loops, conditions, or nested functions
- Only call hooks from React functions - components or custom hooks
- Ensure exhaustive dependencies in useEffect
// Wrong
function BadComponent() {
if (condition) {
useEffect(() => {}); // Conditional hook!
}
}
// Right
function GoodComponent() {
useEffect(() => {
if (condition) {
// Logic inside effect
}
}, []);
}
Key Takeaway
React Hooks revolutionized functional components by adding state and lifecycle capabilities. Master useState, useEffect, and useContext first, then move to optimization hooks like useMemo and useCallback. Create custom hooks to extract and reuse component logic. Always follow the Rules of Hooks to avoid subtle bugs.