Tutorials Logic, IN +91 8092939553 info@tutorialslogic.com
FAQs Support
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Interview Questions Website Development
Compiler Tutorials

Custom Hooks

What are Custom Hooks?

Custom hooks are JavaScript functions that start with use and can call other hooks. They let you extract and reuse stateful logic across multiple components without duplicating code.

Custom hooks are the React way to share logic — they replace patterns like higher-order components and render props.

Custom Hooks — useFetch, useLocalStorage, useDebounce
// hooks/useFetch.js
import { useState, useEffect } from 'react'

function useFetch(url) {
    const [data, setData] = useState(null)
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState(null)

    useEffect(() => {
        if (!url) return
        const controller = new AbortController()
        setLoading(true)
        setError(null)

        fetch(url, { signal: controller.signal })
            .then(res => {
                if (!res.ok) throw new Error(`HTTP ${res.status}`)
                return res.json()
            })
            .then(data => { setData(data); setLoading(false) })
            .catch(err => {
                if (err.name !== 'AbortError') {
                    setError(err.message)
                    setLoading(false)
                }
            })

        return () => controller.abort()
    }, [url])

    return { data, loading, error }
}

// Usage — clean and reusable
function UserList() {
    const { data: users, loading, error } = useFetch('/api/users')

    if (loading) return <p>Loading...</p>
    if (error)   return <p>Error: {error}</p>

    return <ul>{users?.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}

function PostList() {
    const { data: posts, loading } = useFetch('/api/posts')
    // Same hook, different URL — no code duplication!
    return loading ? <p>Loading...</p> : <div>{posts?.length} posts</div>
}
// hooks/useLocalStorage.js
import { useState, useEffect } from 'react'

function useLocalStorage(key, initialValue) {
    const [value, setValue] = useState(() => {
        try {
            const stored = localStorage.getItem(key)
            return stored ? JSON.parse(stored) : initialValue
        } catch {
            return initialValue
        }
    })

    useEffect(() => {
        try {
            localStorage.setItem(key, JSON.stringify(value))
        } catch (err) {
            console.error('localStorage error:', err)
        }
    }, [key, value])

    const remove = () => {
        localStorage.removeItem(key)
        setValue(initialValue)
    }

    return [value, setValue, remove]
}

// Usage
function Settings() {
    const [theme, setTheme] = useLocalStorage('theme', 'light')
    const [fontSize, setFontSize] = useLocalStorage('fontSize', 16)

    return (
        <div>
            <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
                Theme: {theme}
            </button>
            <input type="range" min={12} max={24} value={fontSize}
                onChange={e => setFontSize(Number(e.target.value))} />
            <p style={{ fontSize }}>Font size: {fontSize}px</p>
        </div>
    )
}
import { useState, useEffect, useCallback } from 'react'

// useDebounce — delay value update
function useDebounce(value, delay = 500) {
    const [debouncedValue, setDebouncedValue] = useState(value)
    useEffect(() => {
        const timer = setTimeout(() => setDebouncedValue(value), delay)
        return () => clearTimeout(timer)
    }, [value, delay])
    return debouncedValue
}

// useToggle — boolean toggle
function useToggle(initial = false) {
    const [value, setValue] = useState(initial)
    const toggle = useCallback(() => setValue(v => !v), [])
    return [value, toggle]
}

// useWindowSize — responsive hook
function useWindowSize() {
    const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight })
    useEffect(() => {
        const handler = () => setSize({ width: window.innerWidth, height: window.innerHeight })
        window.addEventListener('resize', handler)
        return () => window.removeEventListener('resize', handler)
    }, [])
    return size
}

// useOnClickOutside — close dropdown/modal
function useOnClickOutside(ref, handler) {
    useEffect(() => {
        const listener = (e) => {
            if (!ref.current || ref.current.contains(e.target)) return
            handler(e)
        }
        document.addEventListener('mousedown', listener)
        return () => document.removeEventListener('mousedown', listener)
    }, [ref, handler])
}

// Usage examples
function SearchBar() {
    const [query, setQuery] = useState('')
    const debouncedQuery = useDebounce(query, 300)
    const { width } = useWindowSize()
    const [isOpen, toggleOpen] = useToggle(false)

    useEffect(() => {
        if (debouncedQuery) console.log('Searching:', debouncedQuery)
    }, [debouncedQuery])

    return (
        <div>
            <input value={query} onChange={e => setQuery(e.target.value)} />
            <p>Window: {width}px | {width < 768 ? 'Mobile' : 'Desktop'}</p>
            <button onClick={toggleOpen}>{isOpen ? 'Close' : 'Open'}</button>
        </div>
    )
}

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.