Одним из самых важных аспектов производительности в приложениях React является то, как ваши компоненты реагируют на изменения. После введения хуков в 2019 году определение компонентов с помощью функций стало новой нормой.
Они появились с интересным побочным эффектом: вся функция выполняется каждый раз, когда React обнаруживает потенциальное изменение в вашем компоненте. Раньше компоненты, определенные с помощью классов, выполняли только определенные методы, такие как методы жизненного цикла (componentDidMount
и т.д.) и хорошо известный метод render
.
Чтобы управлять им, React добавил замечательный хук useEffect
. Однако важно помнить, что функции выполняют весь код внутри, когда их вызывают.
Инициализация состояния в React
Вы можете инициализировать состояние в React с помощью хука useState
:
import { useState } from "react";
const MyComponent = () => {
const [counter, setCounter] = useState(0);
// Increment the given counter
const incrementCounter = () => setCounter(counter + 1);
return (
<section aria-label="Counter">
<button onClick={incrementCounter}>Increment</button>
<output>{counter}</output>
</section>
);
};
MyComponent
определяет новое состояние для управления текущим значением счетчика. Следуя предыдущему утверждению, каждый раз, когда React обнаруживает потенциальное изменение, он вызывает функцию MyComponent
и сравнивает результат выполнения с предыдущим состоянием приложения.
Теперь, если взглянуть на эту функцию глубже, можно обнаружить множество вызовов и определений:
- Вызов
useState
. - Определение функции
incrementCounter
. - Вызов метода JSX под капотом
Кроме этого, есть одна маленькая деталь, о которой обычно забывают. 0
также оценивается. Итак, что произойдет, если вам нужно вызвать функцию для вычисления значения начального состояния?
Ленивое начальное состояние
Теперь давайте проверим следующий код:
import { useState } from "react";
import { initState } from "./utils";
const MyComponent = () => {
const [value, setValue] = useState(initState());
// ...
};
В этом случае useState
получает в качестве параметра не статическое значение, а результат функции. Обратите внимание, что функция initState
вызывается каждый раз, когда React вызывает MyComponent
. Однако функция useState
использует результат только один раз. После его установки следующие выполнения компонента будут отбрасывать результат initState
.
В зависимости от сложности initState
, он может вызвать некоторые проблемы с производительностью в MyComponent
даже после первой инициализации. Чтобы избежать этого, React позволяет передать функцию, которая будет выполнена только один раз:
import { useState } from "react";
import { initState } from "./utils";
const MyComponent = () => {
const [value, setValue] = useState(() => initState());
// ...
};
Этот трюк называется ленивой инициализацией состояния.
Не нужно быть ленивым по умолчанию
Давайте будем справедливы. К счастью, состояния инициализируются статическими значениями большую часть времени. Не все приложения смогут извлечь пользу из этой функции useState
. Тем не менее, это одна из тех проблем производительности, которую трудно обнаружить, а решение довольно простое.
Просто помните об этом, когда вам нужно вызвать функцию для инициализации состояния. И подумайте дважды, если это является обязательным требованием, потому что вашему компоненту все равно придется ждать результата, когда он будет установлен.
Ссылки
- React Hooks
- React useState Hook