Understanding React Hooks: A Practical Guide
Introduction
React is a popular open-source library for building user interfaces more efficiently than traditional tools like vanilla JavaScript or jQuery. One of React game-changing features is Hooks, introduced in React 16.8. Hooks allow developers to use state and other React features without writing class components. This simplifies code and speeds up development.
In this article, we explore six key React hooks: useState, useEffect, useContext, useRef, useCallback, and useMemo. We'll explain what they do, why they are useful, and how to implement them with practical examples.
1. useState: The Foundation of Dynamic Components
useState allows functional components to store and manage state. State enables a component to "remember" information and respond to user interactions. Changes in state or props trigger a re-render, allowing the UI to update dynamically.
Example:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Add 1</button>
<button onClick={() => setCount(count - 1)}>Decrease 1</button>
</div>
);
}Explaination
a. Import useState from React.
b. Initialize state: const [count, setCount] = useState(0).
Here: count → current value
setCount → function to update the value
0 → initial state
c. Updating state: Call setCount(newValue).
Best Practice: Use the callback version to ensure updates are based on the latest state:
setCount(prevCount => prevCount + 1);This avoids issues when multiple updates happen quickly, such as repeated button clicks.
2. useEffect: Handling Side Effects
useEffect is used for side effects - operations that reach outside a component, like doesnt depends on react components:
- Fetching data from an API
- Manipulating the DOM directly
- Setting timers with
setTimeoutorsetInterval
Example:
import { useState, useEffect } from 'react';
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Count has changed!', count);
}, [count]);
}Explanation:
- The first argument: callback to run your effect.
- The second argument: dependency array, which tells React when to run the effect.
Cleanup Function: Sometimes, effects need cleanup when a component unmounts:
useEffect(() => {
const timer = setInterval(() => console.log('Tick'), 1000);
return () => clearInterval(timer); // cleanup
}, []);3. useContext: Avoiding Prop Drilling
useContext helps manage state across multiple levels of components without passing props manually at every level.
Example:
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Navbar />
</ThemeContext.Provider>
);
}
function Navbar() {
const theme = useContext(ThemeContext);
return <h1>Theme: {theme}</h1>;
}Explanation:
Providermakes the value available to all children.useContextallows a child to read the value directly, avoiding prop drilling.
4. useRef: Persistent Values and DOM Access
useRef creates a mutable object whose .current property can hold a value persistently across renders.
Get Anthony Eze’s stories in your inbox
Join Medium for free to get updates from this writer.
Two main use cases:
a) Accessing DOM elements
import React, { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => inputRef.current.focus();
return (
<div>
<input ref={inputRef} />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}b) Storing mutable values
function Counter() {
const [count, setCount] = useState(0);
const renders = useRef(0);
renders.current += 1;
return (
<div>
<p>Count: {count}</p>
<p>Renders: {renders.current}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}Key differences from state:
- Updating a ref does not trigger a re-render.
- Ref updates are synchronous, while state updates are asynchronous.
5. useCallback: Memoizing Functions
useCallback caches a function so it does not get recreated on every render, which is especially useful when passing functions as props to child components.
import { useCallback } from 'react';
import Child from './Child';
function App() {
const handleClick = useCallback(() => {
console.log('Button clicked!');
}, []);
return <Child onClick={handleClick} />;
}- The function is only recreated if dependencies change.
- Helps prevent unnecessary child re-renders.
6. useMemo: Memoizing Values
useMemo caches the result of a calculation instead of a function, preventing expensive recalculations.
import { useMemo } from 'react';
function App({ num }) {
const doubled = useMemo(() => num * 2, [num]);
return <p>Doubled: {doubled}</p>;
}- Use
useMemofor performance optimization when computing values based on changing inputs.
Conclusion
React Hooks provide powerful ways to manage state, side effects, context, and performance in functional components.
useState→ dynamic component stateuseEffect→ side effects and cleanupuseContext→ shared state across componentsuseRef→ persistent mutable values or DOM accessuseCallback→ memoize functionsuseMemo→ memoize computed values
Understanding these hooks gives you the ability to write cleaner, faster, and more maintainable React applications.




