Complete Guide to React Hooks: useState, useEffect, and Custom Hooks
Learn how to use React Hooks effectively with practical examples and best practices for modern React development.
Complete Guide to React Hooks
React Hooks revolutionized how we write React components by allowing us to use state and other React features in functional components. In this comprehensive guide, we'll explore the most commonly used hooks and learn how to create custom hooks.
What are React Hooks?
Hooks are functions that let you "hook into" React state and lifecycle features from functional components. They were introduced in React 16.8 and have become the standard way to write React components.
useState Hook
The useState
hook allows you to add state to functional components.
import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
Best Practices for useState
- Use multiple state variables for unrelated data
- Use functional updates when the new state depends on the previous state
- Initialize state lazily for expensive computations
// Functional update setCount(prevCount => prevCount + 1); // Lazy initialization const [state, setState] = useState(() => { return expensiveComputation(); });
useEffect Hook
The useEffect
hook lets you perform side effects in functional components.
import React, { useState, useEffect } from 'react'; function UserProfile({ userId }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { async function fetchUser() { setLoading(true); try { const response = await fetch(`/api/users/${userId}`); const userData = await response.json(); setUser(userData); } catch (error) { console.error('Failed to fetch user:', error); } finally { setLoading(false); } } fetchUser(); }, [userId]); // Dependency array if (loading) return <div>Loading...</div>; if (!user) return <div>User not found</div>; return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); }
useEffect Patterns
- Effect with cleanup:
useEffect(() => { const subscription = subscribeToSomething(); return () => { subscription.unsubscribe(); }; }, []);
- Conditional effects:
useEffect(() => { if (user) { document.title = `${user.name}'s Profile`; } }, [user]);
Custom Hooks
Custom hooks allow you to extract component logic into reusable functions.
// Custom hook for API calls function useApi(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { async function fetchData() { try { setLoading(true); const response = await fetch(url); if (!response.ok) { throw new Error('Network response was not ok'); } const result = await response.json(); setData(result); } catch (err) { setError(err.message); } finally { setLoading(false); } } fetchData(); }, [url]); return { data, loading, error }; } // Using the custom hook function UserList() { const { data: users, loading, error } = useApi('/api/users'); if (loading) return <div>Loading users...</div>; if (error) return <div>Error: {error}</div>; return ( <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> ); }
Other Useful Hooks
useContext
const theme = useContext(ThemeContext);
useReducer
const [state, dispatch] = useReducer(reducer, initialState);
useMemo and useCallback
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);
Best Practices
-
Follow the Rules of Hooks:
- Only call hooks at the top level
- Only call hooks from React functions
-
Use ESLint plugin: Install
eslint-plugin-react-hooks
to catch common mistakes -
Optimize performance: Use
useMemo
anduseCallback
judiciously -
Keep effects focused: Split unrelated logic into separate
useEffect
calls
Conclusion
React Hooks provide a powerful and flexible way to manage state and side effects in functional components. By mastering useState
, useEffect
, and custom hooks, you'll be able to write cleaner, more maintainable React code.
Remember to follow the rules of hooks and use the ESLint plugin to avoid common pitfalls. Happy coding!