Хуки. Управление функциональными компонентами

Введение в хуки

Последнее обновление: 01.04.2022

Хуки позволяют определять и использовать состояние и другие возможности React без создания классов. По сути хуки представляют функции, которые позволяют подключиться к состоянию и другим возможностям, которые есть в React.

Мы можем создавать свои хуки, однако React по умолчанию уже предоставляет ряд встроенных хуков:

  • useState: предназначен для управления состоянием компонентов

  • useEffect: предназначен для перехвата различного рода изменений в компонентах, которые нельзя обработать внутри компонентов

  • useContext: позволяет подписываться на контекст React

  • useReducer: позволяет управлять локальным состоянием сложных компонентов

  • useCallback: позволяет управлять функциями обратного вызова

  • useMemo: предназначен для управления мемоизированными (грубо говоря кэшированными) значениями

  • useRef: возвращать некоторое изменяемое значение, например, ссылку на html-элементы DOM, которыми затем можно управлять в коде JavaScript

  • useImperativeHandle: настраивает объект, который передается родительскому компоненту при использовании ref

  • useLayoutEffect: аналогичен хуку useEffect(), но вызывается синхронно после всех изменений в структуре DOM

  • useDebugValue: предназначен для отображения некоторого значения в целях отладки

  • useId: предназначен для генерации уникального идентификатора

  • useTransition: применяется для создания переходов при рендеринге

  • useDeferredValue: позволяет отложить рендеринг некритичных частей структуры DOM

  • useSyncExternalStore: предназначен для синхронизации данных с внешними хранилищами

  • useInsertionEffect: предназначен для библиотек, которые используют CSS в JS, для оптимизации при внедрении стилей при рендеринге

Переход от классов к хукам

Рассмотрим простейший пример, как мы можем перейти от классов к хукам. Допустим, у нас есть следующий класс-компонент:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <div id="app"></div>
       
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        class ClickButton extends React.Component {
              
           constructor(props) {
               super(props);
               this.state = {counter: 0};
                  
               this.press = this.press.bind(this);
           }
           incrementCounter(prevState, props) {
                  return {
                    counter: prevState.counter + props.increment
                  };
            }
           press(){
               this.setState(this.incrementCounter);
           }
           render() {
               return <div>
                        <button onClick={this.press}>Count</button>
                        <div>Counter: {this.state.counter}<br /> Increment: {this.props.increment}</div>
                </div>
           }
       }
          
       ReactDOM.createRoot(
            document.getElementById("app")
        )
        .render(
		   <ClickButton increment={2} />
       )
    </script>
</body>
</html>

Здесь определен компонент ClickButton, который принимает через props некоторое значение increment. В конструкторе определяется состояние в виде переменной counter, которая равна 0. Кроме того, в классе определяется метод press(), в котором изменяется состояние компонента.

Для изменения состояния вызывается другой метод - incrementCounter, который берет из props значение increment и использует его для увеличения значения переменной counter.

В коде класса-компонента определяется кнопка, по нажатию на которую как раз и вызывается метод press():

<button onClick={this.press}>Count</button>

В итоге по нажатию на кнопку мы получим увеличение переменной counter:

Переход от классов-компонентов к хукам в React

Теперь определим аналогичный компонент с использованием хуков:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <div id="app"></div>
       
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
		function ClickButtonHook(props){
			const [count, setCount] = React.useState(0);
			const press = function(){
				setCount(count + props.increment);
			};
			return (<div>
					<button onClick={press}>Count</button>
					<div>Counter: {count}<br /> Increment: {props.increment}</div>
				</div>);
		}
          
       ReactDOM.createRoot(
            document.getElementById("app")
        )
        .render(
		   <ClickButtonHook increment={2} />
       )
    </script>
</body>
</html>

В данном случае определен функциональный компонент ClickButtonHook, так как мы не можем использовать хуки в классах-компонентах, а только в функциях.

Вначале определяем переменные состояния:

const [count, setCount] = React.useState(0);

В данном случае определяются две переменных: count и setCount. Переменная count хранит состояние компонента, а переменная setCount позволяет изменять значение переменной count.

В функцию useState() передается число 0 - это то значение, которое по умолчанию получает переменная count.

Для изменения состояния в компоненте определена функция press(), которая выполняет выражение setCount(count + props.increment); - к переменной count прибавляется значение increment из props. Это выражение (count + props.increment) и определяет новое значение переменной count. Таким образом, состояние компонента ClickButtonHook будет изменено.

В коде также определена кнопка, по нажатию на которую вызывается метод press(). В итоге мы получим ту же программу, но с использованием хуков. И как видно, этот код несколько короче, чем код класса-компонента.

Подключение хуков

В примере выше библиотека React подключалась непосредственно на веб-страницу, где и определен весь код приложения. Однако если компоненты расположены в отдельных файлах, то мы можем импортировать хук useState (впрочем как и другие встроенные хуки) следующим образом:

import React, { useState } from "react";

function ClickButtonHook(props){
	const [count, setCount] = React.useState(0);

	const press= () => setCount(count + props.increment);
	return <div>
			<button onClick={press}>Count</button>
			<div>Counter: {count}<br /> Increment: {props.increment}</div>
	</div>;
}

Ограничения при использовании хуков

Хуки имеют ряд ограничений при определении и использовании:

  • Хуки вызываются только на верхнем уровне (top-level) компонента. Они НЕ вызываются внутри циклов, условных конструкций, внутри стандартных функций javascript.

  • Хуки можно вызывать только из функциональных компонентов React, либо из других хуков. Но их нельзя вызывать из классов-компонентов.

    Функциональные компоненты можно определять как обычные функции:

    function ClickButtonHook(props){
    	const [count, setCount] = React.useState(0);
    
    	const press= () => setCount(count + props.increment);
    	return <div>
    			<button onClick={press}>Count</button>
    			<div>Counter: {count}<br /> Increment: {props.increment}</div>
    	</div>;
    }
    

    Либо в виде стрелочных функций:

    const ClickButtonHook = (props)=>{
    	const [count, setCount] = React.useState(0);
    
    	const press= () => setCount(count + props.increment);
    	return <div>
    			<button onClick={press}>Count</button>
    			<div>Counter: {count}<br /> Increment: {props.increment}</div>
    	</div>;
    }
    
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850