React Hooks are functions introduced in React 16.8 that allow developers to use state and lifecycle features in functional components. They simplify code and eliminate the need for class components in many scenarios.
What Are Hooks?
Hooks are special functions that let you “hook into” React features, like state and lifecycle methods, without writing a class.
Why Use Hooks?
- Simplify Components: Replace complex class components with cleaner functional components.
- Reuse Logic: Share stateful logic between components using custom hooks.
- Easier to Read and Test: Functional components with hooks are more concise and easier to test.
Basic Rules of Hooks
- Only Call Hooks at the Top Level
- Do not call hooks inside loops, conditions, or nested functions.
- Always call hooks at the top level of your React function.
- Only Call Hooks in React Functions
- Use hooks in functional components or within custom hooks.
Commonly Used Built-In Hooks
1. useState
- Allows you to add state to a functional component.
- Returns the current state and a function to update it.
import React, { useState } from "react"; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } export default Counter;
2. useEffect
- Performs side effects in functional components (e.g., fetching data, updating the DOM).
- Runs after the render phase by default.
import React, { useState, useEffect } from "react"; function Timer() { const [seconds, setSeconds] = useState(0); useEffect(() => { const interval = setInterval(() => { setSeconds((prev) => prev + 1); }, 1000); return () => clearInterval(interval); // Cleanup on unmount }, []); return <p>Time: {seconds} seconds</p>; } export default Timer;
3. useContext
- Allows components to consume a React context without using
Context.Consumer
.
import React, { createContext, useContext } from "react"; const ThemeContext = createContext("light"); function ThemeButton() { const theme = useContext(ThemeContext); return <button>{theme} Theme</button>; } function App() { return ( <ThemeContext.Provider value="dark"> <ThemeButton /> </ThemeContext.Provider> ); } export default App;
4. useRef
- Returns a mutable object that persists between renders.
- Commonly used to access DOM elements or store state that doesn’t trigger re-renders.
import React, { useRef } from "react"; function FocusInput() { const inputRef = useRef(); const focusInput = () => { inputRef.current.focus(); }; return ( <div> <input ref={inputRef} type="text" /> <button onClick={focusInput}>Focus Input</button> </div> ); } export default FocusInput;
5. useReducer
- An alternative to
useState
for managing more complex state logic.
import React, { useReducer } from "react"; function reducer(state, action) { switch (action.type) { case "increment": return { count: state.count + 1 }; case "decrement": return { count: state.count - 1 }; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, { count: 0 }); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: "increment" })}>+</button> <button onClick={() => dispatch({ type: "decrement" })}>-</button> </div> ); } export default Counter;
Advanced Hooks
1. useMemo
- Optimizes performance by memoizing expensive calculations.
import React, { useState, useMemo } from "react"; function ExpensiveCalculation({ num }) { const result = useMemo(() => { console.log("Calculating..."); return num ** 2; }, [num]); return <p>Square: {result}</p>; } export default ExpensiveCalculation;
2. useCallback
- Memoizes functions to prevent unnecessary re-creation.
import React, { useState, useCallback } from "react"; function Button({ handleClick }) { return <button onClick={handleClick}>Click Me</button>; } function App() { const [count, setCount] = useState(0); const handleIncrement = useCallback(() => { setCount((prev) => prev + 1); }, []); return ( <div> <p>Count: {count}</p> <Button handleClick={handleIncrement} /> </div> ); } export default App;
3. Custom Hooks
- Create reusable logic by combining hooks into custom functions.
import React, { useState } from "react"; function useCounter(initialValue = 0) { const [count, setCount] = useState(initialValue); const increment = () => setCount((prev) => prev + 1); const decrement = () => setCount((prev) => prev - 1); return { count, increment, decrement }; } function Counter() { const { count, increment, decrement } = useCounter(); return ( <div> <p>Count: {count}</p> <button onClick={increment}>+</button> <button onClick={decrement}>-</button> </div> ); } export default Counter;
Hooks Best Practices
- Keep Hooks Simple: Break complex logic into smaller custom hooks.
- Use Dependencies Correctly: Always provide a dependency array in
useEffect
,useCallback
, oruseMemo
. - Avoid Overusing Hooks: Don’t use hooks unnecessarily; keep the component simple.
- Test Hooks: Write unit tests for custom hooks to ensure correctness.
React Hooks empower developers to build cleaner, more efficient, and reusable functional components.