Давайте углубимся в React Hooks — React 16.8
Хуки React – это элементы фреймворка react, которые позволяют нам разрабатывать статические или динамические компоненты и реагировать на них созданием функций путём вызова определённых методов захвата из компонента. Компоненты пользовательского интерфейса, с другой стороны, часто динамичны. Возможно, им придётся изменять состояние своих данных, реагировать на события жизненного цикла и получать доступ к компонентам DOM, среди прочего. Однако до React версии 16.8 разработчикам необходимо было создавать классы для конкретных функциональных возможностей React. Хотя классы всё ещё могут использоваться в React, хуки являются более эргономичным методом проектирования компонентов, поскольку вы можете повторно использовать логику состояния без изменения иерархии компонентов.
В React есть десять встроенных хуков; вы узнаете, что выполняет каждый из них. Хуки – это функции, которые всегда начинаются с названия использования. Вы должны знать, что они могут вызываться только на верхнем уровне функционального компонента и не работают в стандартных функциях JavaScript, циклах вложенных функций или чём-либо ещё. Давайте обсудим их один за другим!
https://t.me/react_tg – React главный телеграм канал!
useState()
Хук useState используется для обработки состояния в функциональном компоненте.Прежде чем возвращать текущее состояние и функцию, которая изменяет состояние в виде пары, useState принимает аргумент для своего начального состояния.
import React, { useState } from 'react';
function Demo() {
// Declare a state variable called "count" with an initial value of 0
const [count, setCount] = useState(0);
// Increase the count by 1 when the button is clicked
const handleClick = () => {
setCount(count + 1);
}
return (
<div>
<p>You clicked the button {count} times.
</p> <button onClick={handleClick}>Click me</button>
</div>
);
}
export default Demo;
Хук useState используется в приведённом выше примере для определения переменной состояния, называемой count, с начальным значением 0. При нажатии кнопки мы дополнительно используем метод setCount для изменения статуса подсчёта.
Хук useState предоставляет массив, содержащий два значения: значение текущего состояния (в данном примере count) и метод для изменения этого значения состояния (setCount). Чтобы присвоить эти значения count и setCount, мы используем деструктурирование массива.
При нажатии кнопки вызывается метод handleClick, а setCount используется для изменения состояния подсчета путём добавления 1 к его текущему значению.
Наконец, мы представляем текущее значение count в теге p вместе с кнопкой, при нажатии на которую активируется метод handleClick.
useEffect()
Этот хук позволяет нам выполнять побочные эффекты в функциональных компонентах. Его первым аргументом является функция обратного вызова, а вторым аргументом является массив зависимых элементов. Функция callback выполняется при вызове компонента, а массив зависимостей определяет, когда эффект должен быть запущен повторно. Одним из наиболее широко используемых хуков в React является useEffect. Он позволяет нам выполнять побочные эффекты в функциональном компоненте, такие как извлечение данных из API, изменение заголовка страницы или подписка на WebSocket.
import React, { useState, useEffect } from 'react';
function Demo() {
const [count, setCount] = useState(0);
useEffect(() => {
// Update the document title with the current count
document.title = `You clicked the button ${count} times`;
}, [count]); // Only re-run the effect if count changes
return (
<div>
<p>You clicked the button {count} times.</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
export default Demo;
В этом примере мы используем useEffect для автоматического обновления заголовка документа с текущим значением при каждом его изменении. Функция effect предназначена для использования эффекта в качестве первого параметра, и она запускается после каждого рендеринга.
Второй параметр для useEffect – это массив зависимостей, который сообщает React, когда эффект следует запустить повторно. В этом примере мы передаём count, что указывает на то, что эффект будет повторно запущен только в том случае, если переменная состояния count изменится.
useEffect запускается после каждого рендеринга по умолчанию, но мы можем изменить время действия эффекта, определив зависимости. Если второй параметр является пустым массивом, эффект произойдёт только один раз при монтировании компонента.
useContext()
Этот хук позволяет нам извлекать близлежащий контекстный объект в функциональном компоненте. Он принимает объект контекста в качестве входных данных и возвращает текущее значение этого контекста.
Хук useContext – это хук React, который позволяет нам использовать контекстную информацию из компонента поставщика, расположенного дальше в дереве компонентов.
Вот пример того, как использовать useContext-хук:
Сначала используйте метод createContext для создания объекта контекста:
import React from 'react';
const ThemeContext = React.createContext('light');
Давайте создадим контекстный объект под названием ThemeContext со значением по умолчанию ‘light’.
Далее нам нужно будет предоставить это контекстное значение дереву компонентов с помощью компонента-поставщика:
import React from 'react';
const ThemeContext = React.createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
Теперь давайте присвоим ThemeContext значение ‘dark’.
Наконец, мы можем использовать это значение контекста в любом дочернем компоненте поставщика, используя хук useContext:
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ backgroundColor: theme === 'dark' ? 'black' : 'white', color: theme === 'dark' ? 'white' : 'black' }}>
{theme}
</button>
);
}
В этом примере мы используем хук useContext для извлечения текущего значения ThemeContext и условного оформления компонента button.
Мы можем просто использовать контекстные значения в любом месте нашего дерева компонентов, используя useContext, избегая необходимости передавать реквизиты вниз по многим слоям компонентов.
useRef()
Хук useRef в React позволяет нам создать изменяемый объект ref, который будет сохраняться в рендеринге. Это полезно для доступа к элементам DOM или хранения значений, которые не вызывают повторной визуализации.
Вот пример использования хука useRef:
import React, { useRef, useEffect } from 'react';
function MyComponent() {
const inputRef = useRef(null);
useEffect(() => {
// Focus the input element on mount
inputRef.current.focus();
}, []);
function handleSubmit(event) {
event.preventDefault();
console.log(inputRef.current.value);
}
return (
<form onSubmit={handleSubmit}>
<input type="text" ref={inputRef} />
<button type="submit">Submit</button>
</form>
);
}
В этом примере мы используем хук useRef для создания изменяемого объекта inputRef с нулевым начальным значением. Затем мы предоставляем этот объект inputRef элементу ввода в качестве ref prop, чтобы мы могли получить доступ к базовому узлу DOM.
Когда компонент монтируется, мы дополнительно используем хук useEffect для фокусировки входного элемента. Это достигается путём выполнения функции focus() для значения inputRef.current, которое присваивается узлу DOM после первоначального рендеринга.
Наконец, когда форма отправлена, мы создаём метод handleSubmit, который регистрирует текущее значение входного элемента. Вызвав inputRef.current.value, мы можем получить текущее значение элемента ввода.
Используя useRef, мы можем легко получить доступ к элементам DOM или сохранить значения, которые не запускают повторный рендеринг.
useReducer()
Хук useReducer – это хук React, который позволяет нам управлять состоянием с помощью функции reducer, аналогичной 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>
</>
);
}
В этом примере мы управляем статусом счётчика с помощью хука useReducer. Мы создаём объект начального состояния без учёта и функцию-редуктор, которая принимает состояние и действие и создаёт новое состояние на основе типа действия.
Компонент Counter инициализирует значения state и dispatch, используя useReducer с функцией reducer и объектом initialState. Затем он отображает текущее значение счётчика и две кнопки, которые при нажатии выполняют операции увеличения и уменьшения.
Функция reducer вызывается с текущим состоянием и действием при отправке действия, и она возвращает новый объект состояния на основе типа действия. Крючок useReducer изменяет значение состояния и повторно отображает компонент с обновлённым значением count.
Мы можем обрабатывать сложную логику состояний с помощью одной функции редуктора и обновлять состояние с помощью доставленных действий, используя useReducer.
useCallback()
Этот хук позволяет нам запоминать функцию так, чтобы она воссоздавалась только при изменении её зависимостей.
Ниже приведён пример того, как использовать хук useCallback:
import React, { useState, useCallback } from 'react';
function ParentComponent() {
const [count, setCount] = useState(0);
// Define a callback function
const handleIncrement = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<h1>Parent Component</h1>
<p>Count: {count}</p>
{/* Pass the callback function as a prop */}
<ChildComponent onIncrement={handleIncrement} />
</div>
);
}
function ChildComponent({ onIncrement }) {
// Use the callback function in the child component
return (
<div>
<h2>Child Component</h2>
<button onClick={onIncrement}>Increment Count</button>
</div>
);
}
У нас есть родительский компонент с состоянием count и функцией обратного вызова handleIncrement, реализованной с помощью хука useCallback. Эта функция обратного вызова передаётся в качестве реквизита дочернему компоненту.
Когда пользователь нажимает на кнопку, мы используем onIncrement prop в дочернем компоненте, чтобы вызвать метод обратного вызова handleIncrement. Мы гарантируем, что метод handleIncrement запоминается и создаётся повторно только в том случае, если значение count изменяется с помощью useCallback, что помогает избежать ненужного повторного рендеринга дочернего компонента.
В целом, useCallback помогает оптимизировать эффективность приложения React за счёт уменьшения количества ненужных повторных отображений.
useMemo()
Мы можем использовать этот хук для запоминания переменной, чтобы она пересчитывалась только при изменении её зависимых элементов. useMemo оптимизирует эффективность за счёт запоминания результата вычисления, так что его не нужно пересчитывать при каждом рендеринге.
Вот пример использования useMemo в функциональном компоненте:
import React, { useMemo, useState } from 'react';
function ExampleComponent() {
const [count, setCount] = useState(0);
// memoize the result of the computation
const expensiveCount = useMemo(() => {
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += count;
}
return result;
}, [count]);
return (
<div>
<p>Count: {count}</p>
<p>Expensive count: {expensiveCount}</p>
<button onClick={() => setCount(count + 1)}>Increment count</button>
</div>
);
}
В этом примере переменная состояния count увеличивается каждый раз, когда пользователь нажимает кнопку “Увеличить количество”. У нас также есть расчёт, выполнение которого занимает много времени (для цикла, который добавляет количество к результату 1 миллиард раз).
Мы можем сохранить результат в памяти, заключив его в useMemo, так что он пересчитывается только при изменении состояния подсчёта. Это подразумевает, что даже если expensiveCount используется в функции рендеринга, это не вызовет повторного рендеринга, если состояние count не изменится.
Это может привести к значительному повышению производительности в крупномасштабных приложениях.
useLayoutEffect()
Этот хук функционирует так же, как useEffect, за исключением того, что он выполняется синхронно после всех обновлений DOM. Это может быть полезно для измерения размера элементов DOM или непосредственного изменения DOM.
Вот пример того, как использовать useLayoutEffect:
import React, { useLayoutEffect, useState } from 'react';
function ExampleComponent() {
const [width, setWidth] = useState(0);
// update the width state variable based on the size of the div element
useLayoutEffect(() => {
const handleResize = () => {
setWidth(divRef.current.offsetWidth);
};
// add resize event listener
window.addEventListener('resize', handleResize);
// initial width measurement
setWidth(divRef.current.offsetWidth);
return () => {
// cleanup function
window.removeEventListener('resize', handleResize);
};
}, []);
// reference to the div element
const divRef = useRef(null);
return (
<div ref={divRef}>
<p>Width: {width}px</p>
</div>
);
}
У нас есть переменная состояния width, которая изменяется в зависимости от ширины элемента. Когда компонент смонтирован, мы используем useLayoutEffect, чтобы добавить прослушиватель событий изменения размера в окно и измерить ширину элемента. Когда компонент размонтирован, мы также очищаем прослушиватель событий.
Переменная divRef – это ссылка на элемент, к которому прикреплен атрибут ref в JSX. Она даёт нам доступ к узлу DOM и позволяет нам измерить его ширину.
В этом примере использование useLayoutEffect вместо useEffect гарантирует, что переменная состояния width будет изменена сразу после изменения ширины элемента из-за события изменения размера. Это позволяет избежать любых проблем с мерцанием или компоновкой, которые могли бы возникнуть, если бы мы вместо этого использовали useEffect.
useImperativeHandle()
useImperativeHandle – это перехватчик React, который позволяет родительскому компоненту получать доступ к функциям и атрибутам экземпляра дочернего компонента. Это полезно в ситуациях, когда родительский компонент должен взаимодействовать с дочерним компонентом определенным образом, например, при запуске анимации или сбросе состояния.
Вот пример того, как использовать useImperativeHandle:
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
const ChildComponent = forwardRef((props, ref) => {
const inputRef = useRef(null);
// expose the setInputValue function to the parent component
useImperativeHandle(ref, () => ({
setInputValue: (value) => {
inputRef.current.value = value;
},
}));
return <input type="text" ref={inputRef} />;
});
function ParentComponent() {
const childRef = useRef(null);
const handleClick = () => {
// set the input value of the child component
childRef.current.setInputValue('Hello, world!');
};
return (
<div>
<button onClick={handleClick}>Set input value</button>
<ChildComponent ref={childRef} />
</div>
);
}
В этом примере у нас есть дочерний компонент с элементом <input>. Родительский компонент должен иметь возможность устанавливать значение элемента ввода путём вызова функции в экземпляре дочернего компонента.
Мы используем useImperativeHandle, чтобы предоставить функцию родительскому компоненту (setInputValue). Эта функция принимает значение в качестве входных данных и использует ссылку для установки элемента <input>.
Чтобы заставить экземпляр дочернего компонента получить доступ к родительскому компоненту, мы используем функцию forwardRef для создания нового компонента, который пересылает ref prop дочернему компоненту.
В родительском компоненте мы создаём переменную childRef с помощью useRef и передаём её дочернему компоненту с помощью ref prop. Мы также определяем функцию handleClick, которая вызывает функцию setInputValue в экземпляре дочернего компонента.
При рендеринге родительского компонента отображается кнопка, при нажатии на которую значение элемента ввода в дочернем компоненте устанавливается равным “Привет, мир!” с помощью функции setInputValue, предоставляемой useImperativeHandle.
useDebugValue()
useDebugValue – это хук React, который отображает пользовательскую отладочную информацию для значения, отслеживаемого другим хуком. Во время разработки это может быть полезно для отслеживания значения переменной состояния или реквизита в компоненте.
Вот пример того, как использовать useDebugValue:
import React, { useState, useDebugValue } from 'react';
function useCustomHook(initialValue) {
const [value, setValue] = useState(initialValue);
// display the current value in the React DevTools
useDebugValue(value);
const increment = () => {
setValue(value + 1);
};
return [value, increment];
}
function ExampleComponent() {
const [count, setCount] = useCustomHook(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
В этом примере у нас есть пользовательский хук (useCustomHook), который записывает переменную состояния (значение) и предлагает метод увеличения для обновления состояния. В React DevTools мы используем useDebugValue для отображения текущего значения переменной.
Мы используем useCustomHook в ExampleComponent для инициализации переменной состояния (count) и функции увеличения. Также определена функция handleClick, которая выполняет метод increment для обновления переменной состояния count.
Текущее значение счётчика отображается на странице при визуализации компонента. Текущее значение также отображается при проверке элемента с помощью React DevTools.
В этом примере использование useDebugValue даёт дополнительную информацию, которая может помочь в отладке и понимании того, как работает пользовательский хук.
Заключение
В этой статье мы рассказали обо всех десяти типах встроенных хуков, используемых в React. Спасибо вам за чтение!
Дополнительные источники
Introducing Hooks — React (reactjs.org)
React Hooks Fundamentals for Beginners (freecodecamp.org)
Building Your Own Hooks — React (reactjs.org)
How to create your own custom React Hooks — LogRocket Blog