Alright, folks—let’s cut the crap and dive into React Hooks, the badass tools that let you ditch class components and write functional components like a pro. I’m not here to sugarcoat shit; I’m here to give you practical examples of the most commonly used hooks so you can apply them in your projects without all the bullshit.
1. useState
– Your Go-To for State Management
If you’re new to React, useState
is where you start. It lets you create state variables in functional components.
Example: A Simple Counter
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
</div>
);
}
export default Counter;
Explanation:
useState(0)
initializescount
with0
.- Clicking the buttons calls
setCount
to update the state. - Simple, effective, and no-fuss.
2. useEffect
– Handling Side Effects Without the Crap
useEffect
is your hook for side effects—fetching data, subscribing to events, or manipulating the DOM.
Example: Fetching Data on Component Mount
import React, { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(res => res.json())
.then(json => setData(json))
.catch(err => console.error('Error fetching data:', err));
}, []); // Empty dependency array: runs once on mount
return (
<div>
{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : <p>Loading data...</p>}
</div>
);
}
export default DataFetcher;
Explanation:
- The empty dependency array
[]
makes sure the effect runs only once (like componentDidMount). - Fetches data from an API, then updates the state.
- If something goes wrong, it logs the error. No excuses.
3. useRef
– Keeping It Mutable Without Re-Renders
useRef
is for storing mutable values that persist between renders without causing a re-render.
Example: Focusing an Input on Mount
import React, { useRef, useEffect } from 'react';
function InputFocus() {
const inputRef = useRef(null);
useEffect(() => {
// Focus the input when component mounts
inputRef.current && inputRef.current.focus();
}, []);
return <input ref={inputRef} placeholder="I get focused on mount" />;
}
export default InputFocus;
Explanation:
useRef(null)
creates a ref object.inputRef.current
gives you direct access to the DOM element.- No state, no re-render—just straight-up DOM manipulation.
4. useContext
– Passing Data Without Prop Drilling
useContext
is perfect for sharing data across your component tree without passing props down manually.
Example: Theme Context
import React, { createContext, useContext } from 'react';
// Create a context with a default value
const ThemeContext = createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme === 'dark' ? '#333' : '#fff', color: theme === 'dark' ? '#fff' : '#000' }}>
I am styled by theme!
</button>
);
}
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
);
}
export default App;
Explanation:
- Create a context using
createContext
. - Use
ThemeContext.Provider
to pass a value down. - Access the value in any component with
useContext(ThemeContext)
.
5. useCallback
– Memoizing Functions to Prevent Unnecessary Renders
When you pass functions to child components, they can trigger unnecessary re-renders. useCallback
helps you avoid that.
Example: Memoizing an Event Handler
import React, { useState, useCallback } from 'react';
function ExpensiveChild({ onClick }) {
console.log('Child rendered');
return <button onClick={onClick}>Click me</button>;
}
function Parent() {
const [count, setCount] = useState(0);
// Memoize the handler so it doesn't get recreated on every render
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
return (
<div>
<h1>Count: {count}</h1>
<ExpensiveChild onClick={handleClick} />
</div>
);
}
export default Parent;
Explanation:
useCallback
ensures thathandleClick
is only recreated if its dependencies change.- Reduces unnecessary re-renders in
ExpensiveChild
.
6. useMemo
– Caching Expensive Computations
useMemo
is like useCallback
but for values. It helps you avoid expensive recalculations on every render.
Example: Memoizing a Heavy Calculation
import React, { useState, useMemo } from 'react';
function HeavyCalculation({ num }) {
// A dummy heavy calculation function
const calculateFactorial = (n) => {
console.log('Calculating factorial...');
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
};
const factorial = useMemo(() => calculateFactorial(num), [num]);
return (
<div>
<p>Factorial of {num} is {factorial}</p>
</div>
);
}
function App() {
const [number, setNumber] = useState(5);
return (
<div>
<input
type="number"
value={number}
onChange={(e) => setNumber(Number(e.target.value))}
/>
<HeavyCalculation num={number} />
</div>
);
}
export default App;
Explanation:
useMemo
caches the result ofcalculateFactorial(num)
untilnum
changes.- Prevents heavy calculations on every render, saving CPU cycles for more important shit.
Final Thoughts
These hooks aren’t just fancy names—they’re tools that help you write cleaner, more efficient React code without the overhead of class components.
useState
: For keeping track of state.useEffect
: For side effects.useRef
: For direct DOM access and mutable values.useContext
: For passing data through the component tree.useCallback
: For memoizing functions.useMemo
: For caching expensive computations.
Practice using these hooks in your own projects. Don’t just watch tutorials—get your hands dirty. Code along with examples, break things, fix them, and then do it again. That’s the only damn way you’ll master this shit.
Cheers, and happy coding, you beautiful code warriors! 🚀🔥
Leave a Reply