Хук useState — подробное руководство PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Хук useState — подробное руководство

Что такое государство?

Прежде чем мы углубимся в хук useState, давайте сначала разберемся с термином состоянии.

Состояние представляет собой информацию о чем-либо в данный момент времени.

Например, давайте рассмотрим текстовое поле.

Изначально внутри этого текстового поля ничего нет, поэтому его состояние равно пустой. Предположим, вы начинаете вводить Hello внутри него, при каждом нажатии клавиши состояние текстового поля будет меняться. Сначала это будет «H», затем «He», затем «Hel» и так далее, пока не станет «Hello».

Кроме того, обратите внимание, что при вводе вы не теряете предыдущее значение. Если вы нажмете «H», а затем «e», вы получите «He», а не просто «e». Другими словами, вы можете думать о состоянии как о Память текстового поля.

Необходимость состояния в компоненте React.

Давайте разберемся в этом с помощью примера.

Codesanbox без состояния

Здесь у нас есть КликСчетчик компонент, который отображает количество нажатий кнопки «Увеличить».

Мы используем локальная переменная «счетчик» вести подсчет кликов.

Каждый раз, когда мы нажимаем кнопку Increment, ручкаклик функция будет вызвана. Эта функция увеличит значение счетчика на 1, а также запишет значение в консоль.

Нажмите кнопку «Увеличить» в предварительном просмотре CodeSandbox.

Ничего не случилось?

Что ж, наша логика кажется правильной. Значение, зарегистрированное в консоли (CodeSandbox), корректно обновляется каждый раз, когда мы нажимаем, но почему это обновление не отражается в пользовательском интерфейсе?

Это из-за того, как работает React.

  • Изменения локальных переменных не вызывают повторный рендеринг.
  • При повторном рендеринге компонент создается с нуля, т.е. функция компонента (в данном примере это функция ClickCounter) выполняется еще раз. Поскольку переменные (например, счетчик) являются локальными для функции, их предыдущие значения теряются.

Так как же заставить компонент запоминать значения между рендерами?

Да, вы правильно поняли! Делаем это с помощью использование состояния крюк.

Хук useState

Хук useState предоставляет механизмы для сохранения состояния и запуска повторного рендеринга.

Давайте посмотрим на его использование.

import React, { useState } from "react";
const state = useState(initialValue);

// OR

const state = React.useState(initialValue);

Хук useState возвращает массив, содержащий два элемента:

  • A переменная состояния который сохраняет свои значения во время повторного рендеринга. Начальное значение, переданное в useState, присваивается переменной состояния во время первого рендеринга.
  • A сеттер функция который обновляет переменную состояния, а также запускает повторный рендеринг.
const state = useState(0);
const data = state[0];
const setData = state[1];

. деструктуризация массива , мы можем реорганизовать приведенные выше операторы в один оператор, как показано ниже:

const [data, setData] = useState(0);

Начальное значение, переданное в useState, используется только во время первого рендеринга. При повторном рендеринге игнорируется.

Счетчик с useState

Теперь давайте обновим предыдущий пример счетчика, включив в него хук useState.

  • Поскольку нам нужно значение счетчика между повторными рендерами, давайте преобразуем его в состояние.
const [counter, setCounter] = useState(0);
  • Вызов setCounter внутри функции handleClick.
const handleClick = () => {
  setCounter(counter + 1);
  console.log(`%c Counter:${counter}`, "color:green");
};

Функция setCounter обновит значение счетчика на 1 и вызовет повторный рендеринг. Когда функция компонента вызывается при повторном рендеринге, переменная состояния, возвращаемая useState, будет иметь обновленное значение.

Попробуйте CodeSandbox с обновленным кодом. Нажмите кнопку Increment и увидите магию useState в действии.

Codesanbox с useState

Вы можете убедиться, что при повторном рендеринге функциональный компонент КликСчетчик вызывается снова при просмотре журналов консоли. Журнал «ClickCounter start», который добавляется в начало компонента, будет регистрироваться при каждом рендере.

первый рендер

перерисовать

Функция обновления

Предположим, мы хотим увеличить значение счетчика на 4 при каждом клике.

const handleClick = () => {
  setCounter(counter + 1);
    setCounter(counter + 1);
    setCounter(counter + 1);
    setCounter(counter + 1);
    console.log(`%c Counter:${counter}`, "color:green");
    };

Предположим, что начальное значение счетчика равно 0. Что вы ожидаете увидеть после нажатия кнопки?

Без функции обновления

Вы ожидали, что счет будет 4, верно? Но почему вместо этого вы видите 1?

а) Каждый рендер связан с состоянием. Значение этого состояния остается заблокированным на время жизни этого рендера.

Обратите внимание, что журнал внутри функции handleClick печатает значение счетчика как 0.

Независимо от того, сколько раз вы вызываете метод setCounter, значение счетчика остается неизменным.

const handleClick = () => {
  setCounter(counter + 1);
    setCounter(counter + 1);
    setCounter(counter + 1);
    setCounter(counter + 1);
    console.log(`%c Counter:${counter}`, "color:green");
    };
б) Пока весь код внутри обработчика событий не будет выполнен, React не вызовет повторный рендеринг.

По этой причине каждый вызов setCounter не запускает отдельный рендеринг. Вместо этого React добавляет эти функции установки в очередь. Выполняет их в том порядке, в котором они были поставлены в очередь. Обновления, внесенные в состояние после выполнения всех операторов, отражаются в следующем рендере. Эта очередь нескольких обновлений состояния известна как дозирующий. Это позволяет React быть более производительным.

Поэтому здесь мы получаем один рендер вместо 4-х разных рендеров.

Этот пример прост, и вы можете решить эту проблему, обновив код, как показано ниже:

const handleClick = () => {
setCounter(counter + 4);
    console.log(`%c Counter:${counter}`, "color:green");
    };

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

Вот где _ обновления _ функция пригодится.

Мы можем реорганизовать предыдущий пример с помощью функции updater следующим образом:

const handleClick = () => {
  setCounter(prevCounter => prevCounter + 1);
    setCounter(prevCounter => prevCounter + 1);
    setCounter(prevCounter => prevCounter + 1);
    setCounter(prevCounter => prevCounter + 1);
    console.log(`%c Counter:${counter}`, "color:green");
    };

Здесь предыдущий счетчик ⇒ предыдущий счетчик + 1 представляет функцию обновления.

Как объяснялось ранее, эти операторы обновления также ставятся в очередь (пакетная обработка).

Функция обновления получает ожидающее/предыдущее состояние, которое она использует для вычисления следующего состояния.

Пакетная обработка функций обновления

Ниже представлен CodeSandbox с добавленной функцией обновления. Попробуйте нажать на кнопку увеличения.

Песочница функции обновления

Функция инициализации

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

import React from "react";
import { useState } from "react";
function ListItems() { 
  const getItems = () => { 
    console.log(`%c getItems called`, "color:hotpink");
    	return Array(50).fill(0); 
    }; 
  const [items, setItems] = useState(getItems()); 
    
    return ( 
    	<div className="card">
        <ul> {items.map((item, index) => 
        	( <li key={index}>Item {index + 1}		</li>))} 
        </ul> <button onClick={() => setItems([...items, 0])}>Add Item</button> 	</div> );
} 
export default ListItems;

Эта функция создает массив размером 50 и заполняет массив нулями. См. изображение ниже.

Массив, заполненный 50 нулями

Затем эти элементы отображаются на экране.

Вроде бы все хорошо, но у нас есть проблема.

Нажмите на Добавить элемент кнопка (расположена после списка элементов), чтобы добавить новый элемент в список. Следите за журналами.

Без функции инициализации

Вы видите здесь проблему?

Журнал «getItems call» добавляется в консоль каждый раз, когда вы добавляете элемент. Это означает, что эта функция вызывается при каждом рендеринге.

Помните, что useState игнорирует начальное значение, переданное ему после первого рендера, но здесь начальное значение все равно пересчитывается. Это может быть дорого, если мы создаем большие массивы или выполняем тяжелые вычисления.

Мы можем решить эту проблему, передав получитьItems как _ инициализатор _ функция.

Теперь давайте внесем небольшое изменение в код.

const [items, setItems] = useState(getItems);

С функцией инициализации

См. окно консоли в CodeSandbox. Обратите внимание, что журнал «getItems call» печатается только при первом рендеринге. При добавлении последующих элементов этого журнала нет.

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

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

✅ const [items, setItems] = useState(getItems);
✅ const [items, setItems] = useState(() => getItems());
❌ const [items, setItems] = useState(getItems());

Сколько у меня может быть хуков useState

Вы можете иметь столько хуков useState внутри компонента, сколько потребуется.

См. CodeSandbox

Несколько хуков useState

Компонент ниже имеет три разных состояния — имя пользователя, пароль, keepMeSignedIn.

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

Галерея

  • useState предоставляет механизм для запуска повторного рендеринга и сохранения состояния между повторными рендерингами.
  • Используйте функцию обновления, когда вам нужно:
    • Вычислить следующее состояние на основе предыдущего состояния.
    • Выполните несколько обновлений состояния перед следующей визуализацией.
  • Если начальное состояние получено из функции – используйте синтаксис функции инициализатора.
  • Внутри компонента может быть несколько хуков useState.

Понравился этот пост? Поделитесь этим с другими.
Первоначально написано для моего личного блога - https://gauravsen.com/use-state-hook

Отметка времени:

Больше от Codementor Реагировать Факт