NarvikHub Logo

NARVIKHUB

Tools

React Hooks Deep Dive

Frontend

2024-09-01

React Hooks Deep Dive: Modern State Management and Side Effects

Master React Hooks from useState to custom hooks, including performance optimization and best practices.

ReactHooksJavaScriptFrontend

React Hooks revolutionized functional components by enabling state and lifecycle features. This comprehensive guide covers all built-in hooks, custom hook patterns, performance optimization, and common pitfalls to avoid.

Essential Hooks

useState - State Management

import { useState } from 'react';

function Counter() {

const [count, setCount] = useState(0);

// Functional update for complex state

const increment = () => setCount(prev => prev + 1);

// Lazy initialization for expensive computation

const [expensiveState] = useState(() => {

return computeExpensiveValue();

});

return (

<button onClick={increment}>

Count: {count}

</button>

);

}

useEffect - Side Effects

import { useEffect, useState } from 'react';

function UserProfile({ userId }) {

const [user, setUser] = useState(null);

const [loading, setLoading] = useState(true);

useEffect(() => {

// Cleanup flag to prevent state updates after unmount

let cancelled = false;

async function fetchUser() {

try {

const response = await fetch(`/api/users/${userId}`);

const data = await response.json();

if (!cancelled) {

setUser(data);

setLoading(false);

}

} catch (error) {

if (!cancelled) {

setLoading(false);

}

}

}

fetchUser();

// Cleanup function

return () => {

cancelled = true;

};

}, [userId]); // Dependency array

return loading ? <div>Loading...</div> : <div>{user?.name}</div>;

}

useContext - Global State

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();

export function ThemeProvider({ children }) {

const [theme, setTheme] = useState('light');

return (

<ThemeContext.Provider value={{ theme, setTheme }}>

{children}

</ThemeContext.Provider>

);

}

export function useTheme() {

const context = useContext(ThemeContext);

if (!context) {

throw new Error('useTheme must be used within ThemeProvider');

}

return context;

}

Performance Hooks

useMemo - Expensive Computations

import { useMemo } from 'react';

function ExpensiveList({ items, filter }) {

// Memoize expensive filtering operation

const filteredItems = useMemo(() => {

console.log('Filtering items...');

return items.filter(item =>

item.name.toLowerCase().includes(filter.toLowerCase())

);

}, [items, filter]); // Only recompute when dependencies change

return (

<ul>

{filteredItems.map(item => (

<li key={item.id}>{item.name}</li>

))}

</ul>

);

}

useCallback - Function Memoization

import { useCallback, useState, memo } from 'react';

// Child component with React.memo

const ExpensiveChild = memo(({ onClick }) => {

console.log('ExpensiveChild rendered');

return <button onClick={onClick}>Click me</button>;

});

function Parent() {

const [count, setCount] = useState(0);

// Without useCallback, this creates new function every render

// const handleClick = () => console.log('Clicked');

// With useCallback, function is memoized

const handleClick = useCallback(() => {

console.log('Clicked');

}, []); // Empty dependencies = never recreated

return (

<div>

<ExpensiveChild onClick={handleClick} />

<button onClick={() => setCount(count + 1)}>

Count: {count}

</button>

</div>

);

}

Advanced Hooks

useReducer - Complex State Logic

import { useReducer } from 'react';

const initialState = { count: 0, history: [] };

function reducer(state, action) {

switch (action.type) {

case 'increment':

return {

count: state.count + 1,

history: [...state.history, state.count + 1]

};

case 'decrement':

return {

count: state.count - 1,

history: [...state.history, state.count - 1]

};

case 'reset':

return initialState;

default:

throw new Error(`Unknown action: ${action.type}`);

}

}

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>

);

}

useRef - DOM References & Mutable Values

import { useRef, useEffect } from 'react';

function TextInput() {

// DOM reference

const inputRef = useRef(null);

// Mutable value that persists across renders

const renderCount = useRef(0);

useEffect(() => {

renderCount.current += 1;

console.log(`Rendered ${renderCount.current} times`);

});

const focusInput = () => {

inputRef.current?.focus();

};

return (

<div>

<input ref={inputRef} type="text" />

<button onClick={focusInput}>Focus Input</button>

</div>

);

}

Custom Hooks

Create reusable logic with custom hooks:

// Custom hook for local storage

function useLocalStorage(key, initialValue) {

const [storedValue, setStoredValue] = useState(() => {

try {

const item = window.localStorage.getItem(key);

return item ? JSON.parse(item) : initialValue;

} catch (error) {

console.error(error);

return initialValue;

}

});

const setValue = (value) => {

try {

setStoredValue(value);

window.localStorage.setItem(key, JSON.stringify(value));

} catch (error) {

console.error(error);

}

};

return [storedValue, setValue];

}

// Custom hook for fetch

function useFetch(url) {

const [data, setData] = useState(null);

const [loading, setLoading] = useState(true);

const [error, setError] = useState(null);

useEffect(() => {

const abortController = new AbortController();

fetch(url, { signal: abortController.signal })

.then(res => res.json())

.then(setData)

.catch(setError)

.finally(() => setLoading(false));

return () => abortController.abort();

}, [url]);

return { data, loading, error };

}

Best Practices

Dependency Arrays

Always include all dependencies in useEffect, useMemo, and useCallback arrays to avoid bugs.

Custom Hook Naming

Always start custom hook names with "use" to enable React's linting rules.

Avoid Premature Optimization

Don't use useMemo and useCallback everywhere. Profile first, optimize second.

Published on 2024-09-01 • Category: Frontend

← Back to Blog

NarvikHub

Free online developer tools and utilities for encoding, formatting, generating, and analyzing data. No registration required - all tools work directly in your browser.

Built for developers, by developers. Privacy-focused and open source.

Popular Tools

Base64 Encoder/DecoderJSON FormatterURL Encoder/DecoderHTML FormatterHash GeneratorUUID Generator

Blog Articles

Base64 Encoding GuideURL Encoding Deep DiveUnderstanding JWT TokensRegular Expressions GuideView All Articles →

Developer Tools & Utilities

Base64 Encoder/DecoderJSON FormatterURL Encoder/DecoderHTML FormatterHash GeneratorUUID GeneratorQR Code GeneratorJWT DecoderTimestamp ConverterRegex TesterText Diff CheckerHex ConverterImage Base64 ConverterASN.1 DecoderCharles Keygen

Free online tools for Base64 encoding, JSON formatting, URL encoding, hash generation, UUID creation, QR codes, JWT decoding, timestamp conversion, regex testing, and more.

Privacy PolicyTerms of ServiceContact

© 2024 NarvikHub. All rights reserved.