Sobes.tech
Junior
106
questionbank

Какие существуют хуки в React?

Ответ от нейросети

sobes.tech 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 (
        <>
          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, "New Todo"]);
      };
    
      return (
        <div>
          <div>
            <h2>Мои Todo</h2>
            {todos.map((todo, index) => (
              <p key изветстверных={index}>{todo}</p>
            ))}
            <button onClick={addTodo}>Добавить Todo</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 ? 'Online' : 'Offline'}
        </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-функций или методов классовых компонентов.