← Назад к вопросам
Как создать кастомный hook?
2.0 Middle🔥 261 комментариев
#React#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Создание кастомных hooks в React
Что такое кастомный hook
Кастомный hook — это JavaScript функция, которая использует встроенные React hooks и возвращает state, логику или другие функции. Это способ переиспользовать логику между компонентами без высокоуровневых компонентов (HOC) и render props.
Правило: имена кастомных hooks должны начинаться с use.
Основная структура
function useFetchData(url) {
const [data, setData] = React.useState(null)
const [loading, setLoading] = React.useState(true)
const [error, setError] = React.useState(null)
React.useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data)
setLoading(false)
})
.catch(err => {
setError(err)
setLoading(false)
})
}, [url])
return { data, loading, error }
}
Использование
function MyComponent() {
const { data, loading, error } = useFetchData('/api/users')
if (loading) return <div>Загрузка...</div>
if (error) return <div>Ошибка: {error.message}</div>
return <ul>{data.map(user => <li key={user.id}>{user.name}</li>)}</ul>
}
Практические примеры
1. useLocalStorage — сохранение в localStorage
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = React.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 {
const valueToStore = value instanceof Function ? value(storedValue) : value
setStoredValue(valueToStore)
window.localStorage.setItem(key, JSON.stringify(valueToStore))
} catch (error) {
console.error(error)
}
}
return [storedValue, setValue]
}
// Использование
function Counter() {
const [count, setCount] = useLocalStorage('count', 0)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
)
}
2. useWindowSize — размер окна
function useWindowSize() {
const [windowSize, setWindowSize] = React.useState({
width: undefined,
height: undefined
})
React.useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight
})
}
window.addEventListener('resize', handleResize)
handleResize()
return () => window.removeEventListener('resize', handleResize)
}, [])
return windowSize
}
// Использование
function ResponsiveComponent() {
const { width } = useWindowSize()
return <div>{width < 768 ? 'Mobile' : 'Desktop'}</div>
}
3. useToggle — переключение boolean
function useToggle(initialState = false) {
const [state, setState] = React.useState(initialState)
const toggle = React.useCallback(() => {
setState(prev => !prev)
}, [])
return [state, toggle]
}
// Использование
function Modal() {
const [isOpen, toggleModal] = useToggle(false)
return (
<>
<button onClick={toggleModal}>Open Modal</button>
{isOpen && <div>Modal Content</div>}
</>
)
}
4. usePrevious — получить предыдущее значение
function usePrevious(value) {
const ref = React.useRef()
React.useEffect(() => {
ref.current = value
}, [value])
return ref.current
}
// Использование
function Counter() {
const [count, setCount] = React.useState(0)
const prevCount = usePrevious(count)
return (
<div>
<p>Current: {count}, Previous: {prevCount}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
)
}
5. useAsync — управление async операциями
function useAsync(asyncFunction, immediate = true) {
const [status, setStatus] = React.useState('idle')
const [data, setData] = React.useState(null)
const [error, setError] = React.useState(null)
const execute = React.useCallback(async () => {
setStatus('pending')
setData(null)
setError(null)
try {
const response = await asyncFunction()
setData(response)
setStatus('success')
return response
} catch (err) {
setError(err)
setStatus('error')
}
}, [asyncFunction])
React.useEffect(() => {
if (immediate) {
execute()
}
}, [execute, immediate])
return { execute, status, data, error }
}
// Использование
function UserProfile({ userId }) {
const { data: user, status, error } = useAsync(
() => fetch(`/api/users/${userId}`).then(r => r.json()),
true
)
if (status === 'pending') return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return <div>{user?.name}</div>
}
6. useDebounce — debouncing значения
function useDebounce(value, delay = 500) {
const [debouncedValue, setDebouncedValue] = React.useState(value)
React.useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => clearTimeout(handler)
}, [value, delay])
return debouncedValue
}
// Использование
function SearchUsers() {
const [search, setSearch] = React.useState('')
const debouncedSearch = useDebounce(search, 300)
React.useEffect(() => {
if (debouncedSearch) {
// Выполняем поиск только после 300ms без изменений
fetchUsers(debouncedSearch)
}
}, [debouncedSearch])
return (
<input
type="text"
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search..."
/>
)
}
Правила кастомных hooks
- Имя начинается с
use— позволяет IDE определить, это hook или нет - Вызывается только на верхнем уровне — не в условиях, циклах или вложенных функциях
- Вызывается только в React компонентах или других hooks — не в обычных функциях
- Используй встроенные hooks — useState, useEffect, useRef, useContext и т.д.
- Делай hooks переиспользуемыми — не привязывай к конкретному компоненту
Best Practices
- Типизация — указывай типы параметров и возвращаемого значения
function useCounter(initial: number = 0): [number, () => void, () => void] {
const [count, setCount] = React.useState(initial)
return [
count,
() => setCount(c => c + 1),
() => setCount(c => c - 1)
]
}
- Документирование — добавляй JSDoc комментарии
/**
* Hook для управления localStorage
* @param {string} key - ключ в localStorage
* @param {*} initialValue - начальное значение
* @returns {[*, Function]} - текущее значение и функция установки
*/
function useLocalStorage(key, initialValue) {
// ...
}
Когда использовать кастомные hooks
- Переиспользование логики между компонентами
- Управление сложным состоянием
- Работа с эффектами и побочными эффектами
- Инкапсуляция логики
- Упрощение компонентов
Кастомные hooks — мощный инструмент для чистого и переиспользуемого кода.