Перейти к содержимому

9. useRef и useReducer

Иллюстрация к уроку В этом уроке мы рассмотрим два мощных хука React: useRef и useReducer. Они позволяют нам работать с DOM-элементами напрямую и управлять сложным состоянием компонентов.

useRef возвращает мутабельный объект ref, который сохраняется на протяжении всего жизненного цикла компонента. Основное применение useRef - получение доступа к DOM-элементам. В отличие от состояния (useState), изменение значения ref не вызывает перерисовку компонента.

import React, { useRef, useEffect } from 'react';
function InputWithFocus() {
// Создаем ref для input элемента
const inputRef = useRef(null);
useEffect(() => {
// После монтирования компонента фокусируемся на input
inputRef.current.focus();
}, []); // Пустой массив зависимостей - useEffect сработает только один раз
return (
<input ref={inputRef} type="text" />
);
}
export default InputWithFocus;

В этом примере inputRef содержит ссылку на DOM-элемент <input>. Мы используем useEffect с пустым массивом зависимостей, чтобы сфокусироваться на input элементе только один раз, после монтирования компонента. inputRef.current содержит ссылку на сам input элемент.

useRef можно использовать и для хранения любых мутабельных значений, которые не должны вызывать перерисовку компонента при изменении. Например, можно хранить счетчик рендеров:

import React, { useRef, useEffect } from 'react';
function RenderCounter() {
const renderCount = useRef(0);
useEffect(() => {
renderCount.current = renderCount.current + 1;
console.log('Компонент перерендерился ', renderCount.current, ' раз');
});
return (
<div>
Этот компонент перерендерился: {renderCount.current} раз. (Не отображается!)
</div>
);
}
export default RenderCounter;

Обратите внимание, что изменение renderCount.current не вызывает перерисовку компонента. Значение обновляется, но компонент не перерисовывается, поэтому текст на экране не меняется.

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

import React, { useReducer } from 'react';
// Редьюсер - функция, которая принимает текущее состояние и action, и возвращает новое состояние
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() {
// useReducer возвращает текущее состояние (state) и функцию dispatch
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
Счетчик: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
export default Counter;

В этом примере reducer определяет, как состояние count должно изменяться при отправке действий increment и decrement. dispatch используется для отправки этих действий редьюсеру.

useRef часто используется для реализации кастомных хуков, например, хука для измерения размера DOM-элемента, или для управления фокусом в формах.

useReducer широко применяется в Redux и других библиотеках управления состоянием. Он также полезен для управления сложным состоянием внутри отдельных компонентов, например, при работе с формами, имеющими множество полей и сложную логику валидации. Многие библиотеки компонентов используют useReducer для управления внутренним состоянием сложных виджетов.

  • useRef позволяет получить доступ к DOM-элементам и хранить мутабельные значения, не вызывая перерисовку.
  • useReducer предназначен для управления сложным состоянием, особенно когда следующее состояние зависит от предыдущего.
  • useReducer использует редьюсер - функцию, определяющую, как состояние изменяется в ответ на действия.
  • useRef не вызывает перерисовку при изменении .current, в отличие от useState.
  • useReducer предоставляет более структурированный способ управления состоянием, чем useState, особенно для сложных сценариев.