Sobes.tech
Junior

Ҳуккҳо дар React чӣ гунаанд?

sobes.tech AI

Ҷавоб аз AI

У React, начиная с версії 16.8, хуки надають функціональним компонентам можливість керувати станом та використовувати життєві цикли, які раніше були доступні лише в класових компонентах.

Ось основні вбудовані хуки:

  • 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 (
        <>
          Кількість: {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: Дозволяє створити мутабельний ref, який зберігається протягом усього життєвого циклу компонента. Часто використовується для доступу до елементів DOM або збереження будь-якого мутабельного значення, яке не викликає перерисовки при зміні.

    // Приклад useRef
    import React, { useRef, useEffect } from 'react';
    
    function TextInputWithFocusButton() {
      const inputEl = useRef(null); // Створення ref
    
      const onButtonClick = () => {
        // current вказує на змонтований DOM-елемент
        inputEl.current.focus();
      };
    
      return (
        <>
          <input ref={inputEl} type="text" /> // Прив'язка ref
          <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) {
          // Обчислення на основі DOM перед перерисовкою
          const { top, height } = ref.current.getBoundingClientRect();
          console.log(`Елемент знаходиться на ${top} з висотою ${height}`);
        }
      }, [children]);
    
      return (
        <span ref={ref} title={text}>
          {children}
        </span>
      );
    }
    
  • useDebugValue: Відображає користувацьку мітку для хуків у React DevTools. Використовується у користувацьких хуках для відлагодження.

    // Приклад 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 ? 'green' : 'black' }}>
          {friend.name} - {isOnline ? 'Онлайн' : 'Офлайн'}
        </li>
      );
    }
    

Крім вбудованих хуків, існують також користувацькі хуки (Custom Hooks). Це JavaScript-функції, імена яких починаються з "use". Вони дозволяють повторно використовувати логіку стану між компонентами. Користувацький хук може викликати інші хуки.

// Приклад користувацького хука
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(); // Використання користувацького хука

  return (
    <p>Розмір вікна: {width}x{height}</p>
  );
}

Важливо дотримуватися Правил хуків:

  1. Викликайте хуки лише на верхньому рівні компонента React або користувацького хука. Не викликайте їх всередині циклів, умов або вкладених функцій.
  2. Викликайте хуки лише з функціональних компонентів React або користувацьких хуків. Не викликайте їх з звичайних JavaScript-функцій або методів класових компонентів.