Хуки. Управление функциональными компонентами
Хуки позволяют определять и использовать состояние и другие возможности 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, для оптимизации при внедрении стилей при рендеринге
Переход от классов к хукам
Рассмотрим простейший пример, как мы можем перейти от классов к хукам. Допустим, у нас есть следующий класс-компонент:
METANIT.COM
Здесь определен компонент ClickButton, который принимает через props некоторое значение increment . В конструкторе определяется состояние в виде переменной counter , которая равна 0. Кроме того, в классе определяется метод press() , в котором изменяется состояние компонента.
Для изменения состояния вызывается другой метод — incrementCounter , который берет из props значение increment и использует его для увеличения значения переменной counter .
В коде класса-компонента определяется кнопка, по нажатию на которую как раз и вызывается метод press() :
В итоге по нажатию на кнопку мы получим увеличение переменной counter :

Теперь определим аналогичный компонент с использованием хуков:
METANIT.COM
В данном случае определен функциональный компонент 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); returnCounter:; >
Increment:Ограничения при использовании хуков
Хуки имеют ряд ограничений при определении и использовании:
- Хуки вызываются только на верхнем уровне (top-level) компонента. Они НЕ вызываются внутри циклов, условных конструкций, внутри стандартных функций javascript.
- Хуки можно вызывать только из функциональных компонентов React, либо из других хуков. Но их нельзя вызывать из классов-компонентов. Функциональные компоненты можно определять как обычные функции:
function ClickButtonHook(props) < const [count, setCount] = React.useState(0); const press= () =>setCount(count + props.increment); returnCounter:; >
Increment:Либо в виде стрелочных функций:
const ClickButtonHook = (props)=> < const [count, setCount] = React.useState(0); const press= () =>setCount(count + props.increment); returnCounter:; >
Increment:Введение — JS: React Hooks
Хуки — механизм в React, который позволяет работать полностью без классов. Он не приносит ничего нового, но облегчает повторное использование кода для решения общих задач. Сейчас это основной способ написания React-приложений. Но хуки не заменяют собой классы целиком. Более того, команда React не планирует удалять поддержку классов, а кое-что без классов сделать не получится. Пример работы хука useState для хранения состояния:
// useState — встроенный в React хук // Подробнее рассматривается в следующем уроке import React, useState > from 'react'; const Example = () => // Пример хука для работы с состоянием const [count, setCount] = useState(0); return ( div> p>Вы нажали count> раз(а)p> button onClick=() => setCount(count + 1)>> Нажми меня button> div> ); >;Хуки — это функции, имена которых принято начинать с use. Так их легко отличить от любых других функций. В React встроено около 10 хуков, из которых только несколько используются постоянно. К основным относятся те, что повторяют функциональность таких классовых компонентов, как работа с состоянием, побочными эффектами (жизненный цикл), контекстом и прямым доступом к DOM. Их мы рассмотрим в курсе. Информацию по остальным хукам можно найти в официальной документации.
Помимо встроенных, в сети можно найти сотни, если уже не тысячи готовых хуков на все случаи жизни. Например, популярная библиотека react-use насчитывает больше 115 хуков. Сейчас разработка на React во многом превратилась в поиск и использование подходящих хуков. Это хорошо, потому что можно сосредоточиться на действительно важных бизнес-задачах и не заниматься изобретением велосипедов.
Как подготовиться к этому курсу
Этот курс предполагает, что студент уже уверенно владеет синтаксисом и методами работы с функциями и объектами, а также имеет базовое представление о React. Если вам нужно освоить или освежить какие-то знания, рекомендуем вам следующие курсы:
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
Что такое хуки и как их использовать: краткий гайд с примерами
Хуки — это технология, которая перехватывает вызовы функций и помогает использовать возможности React без написания классов.
Освітній курс від mate.academy: UI/UX Design.
Творчий розвиток навичок дизайну.С помощью хуков можно написать краткий, лаконичный и более понятный код. И это главное их преимущество. Их использование позволяет изменять любую из функций без необходимости переписывать код целиком. Можно сказать, что хук способен как бы «перезагрузить» функцию.
Внедрение хуков открывает ряд возможностей по отладке кода. Их функциональность упрощает процесс создания и применения логики, с их помощью можно получить доступ к инкапсулированной логике программ. Хуки предоставляют возможность использовать деревья компонентов гораздо меньшего размера, они улучшают структуру кода и его читабельность.
Встроенные хуки
Хуки можно создавать самостоятельно, но в React существует ряд встроенных хуков, которыми можно пользоваться прямо сейчас.
useState()
Хук состояния — useState() — добавляет динамическую логику функциональных компонентов:
Професійний курс від laba: Управління командою в бізнесі.
Створюйте ефективну робочу атмосферу.const [state, setState] = useState(initialState);Хук возвращает функцию и значение состояния для обновления.
Пример. Рендеринг счетчика. При нажатии на кнопку его значение увеличится:
import React, < useState >from 'react'; function Example() < // Объявляем новую переменную состояния "count" const [count, setCount] = useState(0); return (); >Вы нажали раз
Функциональный компонент наделяется внутренним состоянием, которое будет сохраняться между рендерами.
useCallback()
Хук useCallback() оптимизирует рендеринг компонентов:
const memoizedCallback = useCallback( () => < doSomething(a, b); >, [a, b], );А также возвращает мемоизированную версию Callback (когда результат выполнения сохраняется). При этом массив зависимостей не передается.
useContext()
Хук useContext() передает данные дочерним элементам:
const value = useContext(MyContext);А также возвращает текущее значение контекста.
useMemo()
Хук useMemo() позволяет запоминать функции без необходимости их вызова при каждом новом рендеринге:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);А еще — возвращает мемоизированное значение.
Експертний курс від skvot: Unreal Еngine: від інтерфейсу до запуску гри.
Запустіть свою гру з Unreal.Функция, которая передается хуком useMemo , запускается при рендере.
useEffect()
Хук эффекта — useEffect() — выполняет в компонентах функций такие эффекты как вызов API, запросов и подобные:
useEffect(didUpdate);Функция, которую передает useEffect() , запускается только после фиксации рендера на экране.
Пример. Установка компонентом заголовка документа после обновления DOM:
import React, < useState, useEffect >from 'react'; function Example() < const [count, setCount] = useState(0); // По принципу componentDidMount и componentDidUpdate: useEffect(() => < // Обновляем заголовок документа, используя API браузера document.title = `Вы нажали $раз`; >); return (); >Вы нажали раз
React запускает функции с эффектами после каждого рендера.
useReduce()
Хук useReduce() — альтернатива хуку useState() , но используется для сложной логики состояния, управляет внутренним состоянием более сложного компонента:
function Todos() < const [todos, dispatch] = useReducer(todosReducer); // .useRef()
Хук useRef() создает изменяемую переменную (упрощает доступ к элементам React и узлам DOM):
const refContainer = useRef(initialValue);Хук возвращает ref-объект, который будет сохраняться в течение времени жизни самого компонента.
Пример. Доступ к потомку:
function TextInputWithFocusButton() < const inputEl = useRef(null); const onButtonClick = () =>< // `current` указывает на смонтированный элемент `input` inputEl.current.focus(); >; return ( <> type="text" /> >); >Хук useRef() содержит изменяемое значение .current . При каждом рендере хук useRef() дает один и тот же объект с ref .
Стратегічний курс від laba: Business English для фінансистів.
Завоюйте світовий ринок.useImperativeHandle()
Хук useImperativeHandle() настраивает объект при использовании ref , передающийся родительскому компоненту.
Пример. Использование useImperativeHandle с forwardRef:
function FancyInput(props, ref) < const inputRef = useRef(); useImperativeHandle(ref, () =>( < focus: () => < inputRef.current.focus(); >>)); return . />; > FancyInput = forwardRef(FancyInput)Хук useImperativeHandle() контролирует возвращаемое значение. Довольно часто этот хук используют в библиотеках компонентов, где нужна кастомизация поведения элементов DOM.
useDebugValue()
Хук useDebugValue() отображает значение для отладки.
Пример. Создание своего React-хука:
import React, from 'react' function useRandomNumber(min, max) < const [number, setNumber] = useState(null); useEffect(() =>< const range = Math.random() * (max - min) + min setNumber(Math.floor(range)) >, []) useDebugValue(number > 50 ? 'Number more the 50' : 'Number less then 50'); return number; > const UseDebugValuePage = () => < const number = useRandomNumber(1, 100) return () > export default UseDebugValuePageuseDebugValue
Random number:
Хук useDebugValue() будет вызван тогда, когда будет открыт React DevTools.
useLayoutEffect()
Хук useLayoutEffect() — альтернатива хука useEffect() , но вызывается в DOM после изменений в структуре:
import React, from 'react' const UseLayoutEffectPage = () => < const ref = React.useRef() useEffect(() =>< ref.current.value = 'New value' console.log('useEffect'); >) useLayoutEffect(() => < console.log(ref.current.value) >) return () > export default UseLayoutEffectPageuseLayoutEffect
value='Old value' />Хук useLayoutEffect() запускается в React только после фиксации в DOM всех обновлений.
Правила хуков
Хуки — это тоже функции JavaScript , но чтобы их правильно использовать, нужно следовать кое-каким правилам:
- Хуки можно вызывать исключительно на верхнем уровне внутри компонента. Они не могут быть вызваны из вложенных функций или внутри других циклов.
- Хуки вызываются только из функциональных компонентов React.
- Используются вместо классов, внутри них они не работают.
Как создать хуки?
Пусть в компоненте, принадлежащем чату (приложению), отображается уведомление о нахождении пользователя «В сети»:
import React, < useState, useEffect >from 'react'; function FriendStatus(props) < const [isOnline, setIsOnline] = useState(null); useEffect(() => < function handleStatusChange(status) < setIsOnline(status.isOnline); >ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => < ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); >; >); if (isOnline === null) < return 'Загрузка. '; >return isOnline ? 'В сети' : 'Не в сети'; >Давайте сделаем имена пользователей из списка контактов, которые находятся онлайн, зелеными.
Чтобы решить эту задачу, в компонент FriendListItem можно скопировать логику, которая приведена выше:
import React, < useState, useEffect >from 'react'; function FriendListItem(props) < const [isOnline, setIsOnline] = useState(null); useEffect(() => < function handleStatusChange(status) < setIsOnline(status.isOnline); >ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => < ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); >; >); return ( >> ); >Конечно, этот способ совсем не простой и короткий.
Поделимся логикой с FriendStatus и FriendListItem . В React существует два способа разделения логики:
- render props;
- компоненты высшего порядка.
Но использование хуков избавляет от добавления дополнительных функций в дерево компонентов.
Разделить логику между двумя функциями JavaScript можно, если извлечь ее в еще одну, третью функцию.
Первый пользовательский хук — useFriendStatus() :
import < useState, useEffect >from 'react'; function useFriendStatus(friendID) < const [isOnline, setIsOnline] = useState(null); useEffect(() => < function handleStatusChange(status) < setIsOnline(status.isOnline); >ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => < ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); >; >); return isOnline; >Этот хук подписывает нас на статус пользователя. friendID является здесь аргументом:
function useFriendStatus(friendID) < const [isOnline, setIsOnline] = useState(null); // . return isOnline; >Наша изначальная цель — удалить повторяющуюся логику из FriendStatus и FriendListItem .
Когда логика извлечена из useFriendStatus , можно ее использовать:
function FriendStatus(props) < const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) < return 'Loading. '; >return isOnline ? 'Online' : 'Offline'; > function FriendListItem(props) < const isOnline = useFriendStatus(props.friend.id); return ( >> ); >Что здесь произошло? Мы извлекли код из двух функций и поместили в отдельную функцию.
Важно! Используйте use в начале каждого хука. Это нужно для автоматической проверки правил хуков.
Хуки, используемые в двух компонентах, не имеют одинакового состояния. Каждый вызов хука изолирован.
Как мы уже говорили, хуки — это те же функции. А потому можно передавать информацию между ними.
Давайте рассмотрим пример использования компонента, демонстрирующего, находится ли пользователь сейчас онлайн:
const friendList = [ < id: 1, name: 'Ольга' >, < id: 2, name: 'Жанна' >, < id: 3, name: 'Елизавета' >, ]; function ChatRecipientPicker() < const [recipientID, setRecipientID] = useState(1); const isRecipientOnline = useFriendStatus(recipientID); return ( <> /> >); >Идентификатор пользователя сохраняется в recipientID и обновляется в случае выбора в другого пользователя.
Хук useState() возвращает значение recipientID , а значит его можно передать в useFriendStatus в качестве аргумента:
const [recipientID, setRecipientID] = useState(1); const isRecipientOnline = useFriendStatus(recipientID);Так можно узнать, находится ли пользователь в сети.
Пользовательские хуки гибкие, их можно использовать в связке друг с другом.
Ограничения при использовании хуков
При использовании хуков существуют свои ограничения. Их нельзя использовать в классах, внутри условий, вложенных функций и циклов. Именно ограничения отличают хуки от простых функций. Хуки созданы для функциональных компонентов, их можно использовать на первом уровне вложенности. Эти ограничения существуют в самом React во избежание трудно отлаживаемых ошибок, благодаря этому есть возможность определять, какие хуки вызываются, и отслеживать их.
Например, в одном компоненте можно использовать сразу несколько хуков эффектов или состояний:
function Form() < // 1. Use the name state variable const [name, setName] = useState('Mary'); // 2. Use an effect for persisting the form useEffect(function persistForm() < localStorage.setItem('formData', name); >); // 3. Use the surname state variable const [surname, setSurname] = useState('Poppins'); // 4. Use an effect for updating the title useEffect(function updateTitle() < document.title = name + ' ' + surname; >); // . >React определяет порядок, при котором вызываются хуки. Именно так он узнает, какому useState() какое состояние соответствует. Поскольку в примере порядок вызовов одинаков при каждом рендере, он является рабочим:
// ------------ // First render // ------------ useState('Mary') // 1. Initialize the name state variable with 'Mary' useEffect(persistForm) // 2. Add an effect for persisting the form useState('Poppins') // 3. Initialize the surname state variable with 'Poppins' useEffect(updateTitle) // 4. Add an effect for updating the title // ------------- // Second render // ------------- useState('Mary') // 1. Read the name state variable (argument is ignored) useEffect(persistForm) // 2. Replace the effect for persisting the form useState('Poppins') // 3. Read the surname state variable (argument is ignored) useEffect(updateTitle) // 4. Replace the effect for updating the title // .Но что получится, если поместить хук в условие?
// We're breaking the first rule by using a Hook in a condition if (name !== '') < useEffect(function persistForm() < localStorage.setItem('formData', name); >); >Условие (name !== '') имеет статус true для первого рендера. Запускаем хук. Но при следующем рендеринге пользователь может очистить форму, и тогда условие будет false. Порядок вызовов хука изменен:
useState('Mary') // 1. Read the name state variable (argument is ignored) // useEffect(persistForm) // This Hook was skipped! useState('Poppins') // 2 (but was 3). Fail to read the surname state variable useEffect(updateTitle) // 3 (but was 4). Fail to replace the effectТеперь каждый последующий вызов хука будет сдвигаться на единицу, возникнут ошибки. Вот почему хуки должны вызываться на верхнем уровне компонентов.
Компоненты чаще всего определяются как обычные функции:
function ClickButtonHook(props) < const [count, setCount] = React.useState(0); const press= () =>setCount(count + props.increment); returnCounter:; >
Increment:Также они могут определяться в виде стрелочных функций:
const ClickButtonHook = (props)=> < const [count, setCount] = React.useState(0); const press= () =>setCount(count + props.increment); returnCounter:; >
Increment:Как подключать хуки
При условии, если компоненты расположены в отдельных файлах, встроенные хуки можно подключить, используя такой способ:
import React, < useState >from "react"; function ClickButtonHook(props) < const [count, setCount] = React.useState(0); const press= () =>setCount(count + props.increment); returnCounter:; >
Increment:Заключение
В сравнении с классами, у хуков есть ряд своих преимуществ. Упрощается логика жизненного цикла, появляются возможности гибкой оптимизации (мемоизация), нет необходимости сохранять имена методов и работать с прототипами.
Кастомные хуки позволяют быстрее и легче шейрить логику (она сразу закладывается в хуки). Это ведет к уменьшению вложенности дерева компонентов. React получает возможность пристальнее отслеживать изменения в компонентах.
Код, в котором используются хуки, прекрасно поддается минимизации. Хуки позволяют выделить одну логику, которая будет управлять определенным побочным эффектом.
Без них такая логика при наличии нескольких побочных эффектов в компоненте разбивалась бы на разные методы жизненного цикла. А благодаря мемоизации компоненты не будут обновлены или пересозданы. В memo можно поместить все функциональные компоненты.
Но наравне с широкими возможностями хуки имеют и ограничения. Стоит хорошо разобраться в том, как они работают, особенно понять, что такое мемоизация. Только тогда можно научиться писать читабельный и лаконичных код с использованием хуков.
Краткий обзор хуков
Хуки — нововведение в React 16.8, которое позволяет использовать состояние и другие возможности React без написания классов.
Хуки — обратно совместимы. На этой странице вы получите общее представление о хуках. Имейте в виду, что это беглый обзор, который больше подойдёт опытным пользователям React. В конце каждого раздела есть вот такой жёлтый блок с детальным объяснением на случай, если вы запутались:
Подробное объяснение
Если вы хотите понять, почему мы добавляем хуки в React, прочтите мотивацию.
Рассмотрим пример, в котором рендерится счётчик. Если вы нажмёте на кнопку, значение счётчика будет инкрементировано.
import React, useState > from 'react'; function Example() // Объявляем новую переменную состояния "count" const [count, setCount] = useState(0); return ( div> p>Вы нажали count> разp> button onClick=() => setCount(count + 1)>> Нажми на меня button> div> ); >В этом примере, useState — это хук (определение хука дано ниже). Мы вызываем его, чтобы наделить наш функциональный компонент внутренним состоянием. React будет хранить это состояние между рендерами. Вызов useState возвращает массив с двумя элементами, который содержит: текущее значение состояния и функцию для его обновления. Эту функцию можно использовать где угодно, например, в обработчике событий. Она схожа с this.setState в классах, но не сливает новое и старое состояние вместе. Сравнение хука useState и this.state приводится на странице Использование хука состояния.
Единственный аргумент useState — это начальное состояние. В примере выше — это 0 , так как наш счётчик начинается с нуля. Заметьте, что в отличие от this.state , в нашем случае состояние может, но не обязано, быть объектом. Исходное значение аргумента используется только при первом рендере.
Объявление нескольких переменных состояния
Хук состояния можно использовать в компоненте более одного раза.
function ExampleWithManyStates() // Объявляем несколько переменных состояния! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('банан'); const [todos, setTodos] = useState([ text: 'Изучить хуки' >]); // . >Синтаксис деструктуризации массивов позволяет нам по-разному называть переменные состояния, которые мы объявляем при вызове useState . Так как имён этих переменных нет в API useState , React предполагает, что если вы вызываете useState много раз, вы делаете это в одинаковой последовательности при каждом рендере. Мы расскажем, почему это работает и когда это целесообразно, немного позже.
Что же такое хук?
Хуки — это функции, с помощью которых вы можете «подцепиться» к состоянию и методам жизненного цикла React из функциональных компонентов. Хуки не работают внутри классов — они дают вам возможность использовать React без классов. (Мы не рекомендуем сразу же переписывать существующие компоненты, но при желании, вы можете начать использовать хуки в своих новых компонентах.)
React содержит несколько встроенных хуков, таких как useState . Вы также можете создавать собственные хуки, чтобы повторно использовать их в других своих компонентах. Давайте для начала рассмотрим встроенные хуки.
Вам скорее всего доводилось ранее запрашивать данные, делать подписки или вручную менять DOM из React-компонента. Мы расцениваем эти операции как «побочные эффекты» (или сокращённо «эффекты»), так как они могут влиять на работу других компонентов и их нельзя выполнить во время рендера.
С помощью хука эффекта useEffect вы можете выполнять побочные эффекты из функционального компонента. Он выполняет ту же роль, что и componentDidMount , componentDidUpdate и componentWillUnmount в React-классах, объединив их в единый API. Вы можете найти сравнение useEffect и этих методов на странице использование хука эффекта.
К примеру, этот компонент устанавливает заголовок документа после того, как React обновляет DOM:
import React, useState, useEffect > from 'react'; function Example() const [count, setCount] = useState(0); // По принципу componentDidMount и componentDidUpdate: useEffect(() => // Обновляем заголовок документа, используя API браузера document.title = `Вы нажали $count> раз`; >); return ( div> p>Вы нажали count> разp> button onClick=() => setCount(count + 1)>> Нажми на меня button> div> ); >Когда вы вызываете useEffect , React получает указание запустить вашу функцию с «эффектом» после того, как он отправил изменения в DOM. Поскольку эффекты объявляются внутри компонента, у них есть доступ к его пропсам и состоянию. По умолчанию, React запускает эффекты после каждого рендера, включая первый рендер. Мы рассмотрим более подробно, как это отличается от классовых методов жизненного цикла на странице использование хука эффекта.
При необходимости вы можете вернуть из эффекта функцию, которая указывает эффекту, как выполнить за собой «сброс». Например, этот компонент использует эффект, чтобы подписаться на статус друга в сети, и выполняет сброс, отписываясь от него.
import React, useState, useEffect > from 'react'; function FriendStatus(props) const [isOnline, setIsOnline] = useState(null); function handleStatusChange(status) setIsOnline(status.isOnline); > useEffect(() => ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); >; >); if (isOnline === null) return 'Загрузка. '; > return isOnline ? 'В сети' : 'Не в сети'; >В этом примере, React будет отписываться от нашего ChatAPI перед тем, как компонент размонтируется и перед тем, как перезапустить эффект при повторном рендере. Вы можете сделать так, чтобы React пропускал повторные подписки если props.friend.id , который мы передали в ChatAPI , остался без изменений.
Так же как и useState , вы можете использовать более одного эффекта в компоненте:
function FriendStatusWithCounter(props) const [count, setCount] = useState(0); useEffect(() => document.title = `Вы нажали $count> раз`; >); const [isOnline, setIsOnline] = useState(null); useEffect(() => ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); >; >); function handleStatusChange(status) setIsOnline(status.isOnline); > // .Хуки дают вам возможность организовать побочные эффекты в компоненте по связанным частям (например, добавление или отмена подписки), вместо того, чтобы принуждать вас делить всё согласно методам жизненного цикла.
Подробное объяснение
Вы можете узнать больше о useEffect на странице Использование хука эффекта.
✌️ Правила хуков
Хуки — это функции JavaScript, которые налагают два дополнительных правила:
- Хуки следует вызывать только на верхнем уровне. Не вызывайте хуки внутри циклов, условий или вложенных функций.
- Хуки следует вызывать только из функциональных компонентов React. Не вызывайте хуки из обычных JavaScript-функций. Есть только одно исключение, откуда можно вызывать хуки — это ваши пользовательские хуки. Мы расскажем о них далее.
Мы разработали специальный плагин для линтера, который помогает обеспечивать соблюдение этих правил. Мы понимаем, что эти правила могут показаться немного непонятными и накладывать определённые ограничения, но они очень важны для правильной работы хуков.
Подробное объяснение
Вы можете узнать больше на странице Правила хуков.
Создание собственных хуков
Иногда нужно повторно использовать одинаковую логику состояния в нескольких компонентах. Традиционно использовались два подхода: компоненты высшего порядка и рендер-пропсы. С помощью пользовательских хуков эта задача решается без добавления ненужных компонентов в ваше дерево.
Ранее на этой странице мы рассматривали компонент FriendStatus , который вызывал хуки useState и useEffect , чтобы подписаться на статус друга в сети. Допустим, мы хотим ещё раз использовать эту логику с подпиской, но уже в другом компоненте.
Прежде всего, давайте извлечём эту логику в пользовательский хук useFriendStatus
import React, useState, useEffect > from 'react'; function useFriendStatus(friendID) const [isOnline, setIsOnline] = useState(null); function handleStatusChange(status) setIsOnline(status.isOnline); > useEffect(() => ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); >; >); return isOnline; >Хук принимает friendID в качестве аргумента и возвращает переменную, которая показывает, в сети наш друг или нет.
Теперь мы можем использовать этот хук в обоих наших компонентах:
function FriendStatus(props) const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) return 'Загрузка. '; > return isOnline ? 'В сети' : 'Не в сети'; >function FriendListItem(props) const isOnline = useFriendStatus(props.friend.id); return ( li style= color: isOnline ? 'green' : 'black' >>> props.friend.name> li> ); >Состояния каждого компонента никаким образом не зависят друг от друга. Хуки — это способ использовать повторно логику состояния, а не само состояние. Более того, каждое обращение к хуку обеспечивает совершенно изолированное состояние. Вы даже можете использовать один и тот же хук несколько раз в одном компоненте.
Пользовательские хуки — это в большей степени соглашение, чем дополнение. Если имя функции начинается с ” use ” и она вызывает другие хуки, мы расцениваем это как пользовательский хук. Если вы будете придерживаться соглашения useSomething при именовании хуков, это позволит нашему плагину для линтера найти баги в коде, который использует хуки.
Есть много подходящих случаев, чтобы написать пользовательские хуки, такие как работа с формами, анимация, декларативные подписки, таймеры и, наверное, много других, о которых мы даже не думали. Мы с нетерпением ожидаем увидеть, какие же пользовательские хуки сообщество React сможет придумать.
Есть ещё несколько менее используемых встроенных хуков, которые могут вам пригодиться. Например, с помощью useContext , вы можете подписаться на контекст React без использования каких-либо вложений.
function Example() const locale = useContext(LocaleContext); const theme = useContext(ThemeContext); // . >А хук useReducer даёт возможность управлять внутренним состоянием более сложного компонента с помощью редюсера.
function Todos() const [todos, dispatch] = useReducer(todosReducer); // .Подробное объяснение
Вы можете узнать больше обо всех встроенных хуках на странице API-справочника хуков.
Фух, давайте перестанем торопиться и немного охладим пыл! Если вам что-то непонятно или вы хотите узнать о чём-либо более подробно, вы можете начать читать следующие страницы, начиная с документации хука состояния.
Вы также можете просмотреть API-справочник хуков и FAQ хуков.
И наконец, не проходите мимо вступительной страницы, на которой вы узнаете почему мы добавляем хуки и как мы планируем использовать их вместе с классами без необходимости переписывать наши приложения.
