Кои са хуковете в React?
sobes.tech AI
Отговор от AI
В React, започвайки от версия 16.8, hook-овете предоставят възможност на функционалните компоненти да управляват състоянието и да използват жизнените цикли, които преди това бяха достъпни само за класовите компоненти.
Ето основните вградени hook-ове:
-
useState: Позволява добавяне на вътрешно състояние към функционален компонент. Връща двойка: текущото състояние и функция за неговото обновяване.// Пример useState import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); // Инициализация на състоянието с 0 return ( <div> <p>Натиснахте {count} пъти</p> <button onClick={() => setCount(count + 1)}> Натисни ме </button> </div> ); } -
useEffect: Позволява изпълнение на странични ефекти във функционалните компоненти. Подобен е на методите за жизнения цикъл на класовите компоненти (componentDidMount,componentDidUpdate,componentWillUnmount).// Пример useEffect import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // Изпълнява се след всеки рендер useEffect(() => { document.title = `Натиснахте ${count} пъти`; }); // Изпълнява се само при първия рендер (празен масив от зависимости) useEffect(() => { console.log('Компонентът е монтиран'); return () => { console.log('Компонентът е демонтиран'); // Функция за почистване }; }, []); // Изпълнява се при промяна на count useEffect(() => { console.log('Count се промени:', count); }, [count]); return ( <div> <p>Натиснахте {count} пъти</p> <button onClick={() => setCount(count + 1)}> Натисни ме </button> </div> ); } -
useContext: Позволява абониране за промени в React контекста. Приема обект на контекст, създаден чрезReact.createContext().// Пример useContext import React, { useContext } from 'react'; const ThemeContext = React.createContext('light'); // Създаване на контекст function ThemedButton() { const theme = useContext(ThemeContext); // Използване на контекста return ( <button className={theme}> Бутон в тема {theme} </button> ); } function App() { return ( <ThemedButton /> ); } -
useReducer: Алтернатива наuseStateза управление на по-сложно състояние, което включва няколко под-стойности или когато следващото състояние зависи от предишното. Подобен е на редуисерите в Redux.// Пример useReducer import React, { useReducer } from 'react'; const initialState = { count: 0 }; function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> </> ); } -
useCallback: Мемоизира функция за обратно извикване. Връща мемоизирана версия на колбека, която се променя само при промяна на една от зависимостите. Полезно за предотвратяване на ненужни повторни рендери на дъщерни компоненти при предаване на колбэкове.// Пример useCallback import React, { useState, useCallback } from 'react'; function Parent({ addTodo }) { const [count, setCount] = useState(0); const handleAddTodo = useCallback(() => { addTodo(); }, [addTodo]); // зависимост от addTodo return ( <> <button onClick={() => setCount(count + 1)}> Увеличи брояча </button> <Child onAddItem={handleAddTodo} /> </> ); } function Child({ onAddItem }) { console.log('Child се рендерира'); // Ще се рендерира само при промяна на onAddItem return <button onClick={onAddItem}>Добави елемент</button>; } -
useMemo: Мемоизира изчисляемата стойност. Връща мемоизирана стойност, която се пресмята само при промяна на една от зависимостите. Полезно за производителност при тежки изчисления.// Пример useMemo import React, { useMemo, useState } from 'react'; function expensiveCalculation(num) { console.log('Извършва се тежко изчисление...'); for (let i = 0; i < 1000000; i++) { num += 1; } return num; } function Example() { const [count, setCount] = useState(0); const [todos, setTodos] = useState([]); const calculation = useMemo(() => expensiveCalculation(count), [count]); // зависимост от count const addTodo = () => { setTodos(t => [...t, "Нов списък"]); }; return ( <div> <div> <h2>Моите задачи</h2> {todos.map((todo, index) => ( <p key={index}>{todo}</p> ))} <button onClick={addTodo}>Добави задача</button> </div> <hr /> <div> Брой: {count} <button onClick={() => setCount(c => c + 1)}>+</button> <h2>Тежко изчисление</h2> {calculation} </div> </div> ); } -
useRef: Позволява създаване на мутабелен реф, който се запазва през целия жизнен цикъл на компонента. Често се използва за достъп до DOM елементи или за съхраняване на всяка мутабелна стойност, която не предизвиква повторно рендериране при промяна.// Пример useRef import React, { useRef } from 'react'; function TextInputWithFocusButton() { const inputEl = useRef(null); // създаване на реф const onButtonClick = () => { inputEl.current.focus(); // фокусиране върху DOM елемента }; return ( <> <input ref={inputEl} type="text" /> // свързване на реф <button onClick={onButtonClick}>Фокус върху полето</button> </> ); } -
useImperativeHandle: Настройва дескриптор на инстанция, който се предоставя на родителския компонент при използване наref. Позволява на дъщерния компонент да разкрива определени методи на родителя. Използва се заедно сforwardRef.// Пример useImperativeHandle import React, { useRef, useImperativeHandle, forwardRef } from 'react'; const MyInput = forwardRef((props, ref) => { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focusInput: () => { inputRef.current.focus(); } })); return <input ref={inputRef} {...props} />; }); function ParentComponent() { const inputRef = useRef(); const handleFocus = () => { inputRef.current.focusInput(); // метод от дъщерния компонент }; return ( <div> <MyInput ref={inputRef} /> <button onClick={handleFocus}>Фокус чрез Parent</button> </div> ); } -
useLayoutEffect: Подобен наuseEffect, но се изпълнява синхронно след всички мутации в DOM. Полезен за четене на данни от DOM или за извършване на мутации, които трябва да се случат преди браузърът да изрисува екрана. Може да блокира рендерирането.// Пример useLayoutEffect import React, { useLayoutEffect, useRef } from 'react'; function Tooltip({ children, text }) { const ref = useRef(null); useLayoutEffect(() => { if (ref.current) { const { top, height } = ref.current.getBoundingClientRect(); console.log(`Елементът е на ${top} с височина ${height}`); } }, [children]); return ( <span ref={ref} title={text}> {children} </span> ); } -
useDebugValue: Показва потребителска метка за hook-ове в React DevTools. Използва се при създаване на собствени hook-ове за отстраняване на грешки.// Пример useDebugValue import React, { useState, useDebugValue } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); // Показва "Friend Status: Offline" или "Friend Status: Online" в DevTools useDebugValue(isOnline === true ? 'Online' : 'Offline'); // ... логика за получаване на статуса на приятеля return isOnline; } function FriendListItem({ friend }) { const isOnline = useFriendStatus(friend.id); return ( <li style={{ color: isOnline ? 'зелен' : 'черен' }}> {friend.name} - {isOnline ? 'Онлайн' : 'Изключен'} </li> ); }
Освен вградени hook-ове, съществуват и потребителски hook-ове (Custom Hooks). Това са JavaScript функции, чиито имена започват с "use". Те позволяват повторно използване на логика за състояние между компоненти. Потребителският hook може да извиква други hook-ове.
// Пример потребителски hook
import { useState, useEffect } from 'react';
function useWindowSize() {
const [size, setSize] = useState([window.innerWidth, window.innerHeight]);
useEffect(() => {
const handleResize = () => {
setSize([window.innerWidth, window.innerHeight]);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return size;
}
function MyComponent() {
const [width, height] = useWindowSize(); // използване на потребителски hook
return (
<p>Размер на прозореца: {width}x{height}</p>
);
}
Важно е да се спазват правилата за hook-ове:
- Извиквайте hook-ове само на върховото ниво на React компонента или на потребителски hook. Не ги извиквайте вътре в цикли, условия или вложени функции.
- Извиквайте hook-ове само от React функционални компоненти или потребителски hook-ове. Не ги извиквайте от обикновени JavaScript функции или методи на класови компоненти.