Содержание туториала по React
Используйте инструменты разработчика React для инспектирования компонентов React, редактирования пропсов и состояния, а также выявления проблем с производительностью.
Расширение для браузера
Самый простой способ отладки веб-сайтов, созданных с помощью React — установить расширение для браузера React Developer Tools. Он доступен для нескольких популярных браузеров:
Теперь, если вы посетите веб-сайт, созданный с помощью React, вы увидите панели Components и Profiler.
Safari и другие браузеры
Для других браузеров (например, Safari) установите npm-пакет react-devtools
:
# Yarn
yarn global add react-devtools
# Npm
npm install -g react-devtools
Затем откройте инструменты разработчика из терминала:
react-devtools
Затем подключите свой веб-сайт, добавив следующий тег <script>
в начало <head>
вашего веб-сайта:
<html>
<head>
<script src="http://localhost:8097"></script>
</head>
</html>
Перезагрузите свой веб-сайт в браузере, чтобы просмотреть его в инструментах разработчика.
Мобильный (React Native)
Инструменты разработчика React также можно использовать для проверки приложений, созданных с помощью React Native.
Самый простой способ использовать React Developer Tools — установить его глобально:
# Yarn
yarn global add react-devtools
# Npm
npm install -g react-devtools
Затем откройте инструменты разработчика из терминала.
react-devtools
Он должен подключаться к любому локальному приложению React Native, которое запущено.
Попробуйте перезагрузить приложение, если инструменты разработчика не подключаются через несколько секунд. Очередь обновлений состояния в React
Содержание туториала по React
Установка переменной состояния поставит в очередь следующий рендеринг. Но иногда нужно выполнить несколько операций над значением перед постановкой в очередь следующего рендеринга. Для этого полезно понять, как React пакетно обновляет состояние (батчинг).
React батчит обновления состояний
Батчинг - это объединение обновлений в одну операцию.
В следующем примере, вы можете ожидать, что нажатие кнопки «+3» увеличит счетчик три раза, потому что он трижды вызывает setNumber(number + 1)
:
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button
onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}}
>
+3
</button>
</>
);
}
Однако, как вы, возможно, помните из предыдущего раздела, значения состояния каждого рендеринга фиксированы, поэтому значение number
внутри обработчика событий первого рендеринга всегда равно 0
, независимо от того, сколько раз вы вызываете setNumber(1)
:
setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);
Но здесь есть еще один фактор, который нужно обсудить. React ждет, пока весь код в обработчиках событий будет запущен, прежде чем обрабатывать ваши обновления состояния. Вот почему повторный рендеринг происходит только после всех этих вызовов setNumber()
.
Это может напомнить вам официанта, принимающего заказ в ресторане. Официант не бежит на кухню при упоминании вашего первого блюда. Вместо этого они позволяют вам закончить свой заказ, позволяют вносить в него изменения и даже принимать заказы от других людей за столом.
Это позволяет вам обновлять несколько переменных состояния — даже из нескольких компонентов — без запуска слишком большого количества повторных рендерингов. Но это также означает, что пользовательский интерфейс не будет обновляться до тех пор, пока ваш обработчик событий и любой код в нем не завершится. Такое поведение, также известное как пакетная обработка (батчинг), позволяет вашему приложению React работать намного быстрее. Это также позволяет избежать сбивающих с толку «недоделанных» рендеров, в которых были обновлены только некоторые переменные.
React не объединяет несколько преднамеренных событий, таких как клики, — каждый клик обрабатывается отдельно. Будьте уверены, что React выполняет пакетную обработку только тогда, когда это в целом безопасно. Это гарантирует, что, например, если первый клик кнопки отключит форму, второй щелчок не отправит ее снова.
Обновление одной и той же переменной состояния несколько раз перед следующим рендерингом
Это необычный вариант использования, но если вы хотите обновить одну и ту же переменную состояния несколько раз перед следующим рендерингом, вместо передачи следующего значения состояния, такого как setNumber(number + 1)
, вы можете передать функцию, которая вычисляет следующее состояние. на основе предыдущего в очереди, например setNumber(n => n + 1)
. Это способ сказать React «сделать что-нибудь со значением состояния», а не просто заменить его.
Вот как это выглядит на практике:
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button
onClick={() => {
setNumber((n) => n + 1);
setNumber((n) => n + 1);
setNumber((n) => n + 1);
}}
>
+3
</button>
</>
);
}
Здесь n => n + 1
называется функцией обновления. Когда вы передаете его установщику состояния:
- React ставит эту функцию в очередь для обработки после выполнения всего остального кода в обработчике событий.
- Во время следующего рендеринга React проходит через очередь и выдает вам окончательное обновленное состояние.
setNumber((n) => n + 1);
setNumber((n) => n + 1);
setNumber((n) => n + 1);
Вот как React работает с этими строками кода при выполнении обработчика событий:
setNumber(n => n + 1)
: n => n + 1
— это функция. React добавляет его в очередь.
setNumber(n => n + 1)
: n => n + 1
— это функция. React добавляет его в очередь.
setNumber(n => n + 1)
: n => n + 1
— это функция. React добавляет его в очередь.
Когда вы вызываете useState
во время следующего рендеринга, React проходит через очередь. Предыдущее числовое состояние было 0
, так что это то, что React передает первой функции обновления в качестве аргумента n
. Затем React берет возвращаемое значение вашей предыдущей функции обновления и передает его следующему обновлению как n
и так далее:
запланированное обновление | n | возвращает |
---|
n => n + 1 | 0 | 0 + 1 = 1 |
n => n + 1 | 1 | 1 + 1 = 2 |
n => n + 1 | 2 | 2 + 1 = 3 |
React сохраняет 3
в качестве конечного результата и возвращает его из useState
.
Вот почему нажатие «+3» в приведенном выше примере корректно увеличивает значение на 3
.
Что произойдет, если вы обновите состояние после его замены
А как насчет этого обработчика событий? Как вы думаете, какое число будет в следующем рендере?
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
}}>
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button
onClick={() => {
setNumber(number + 5);
setNumber((n) => n + 1);
}}
>
Increase the number
</button>
</>
);
}
Вот что этот обработчик событий говорит React:
setNumber(number + 5)
: number
равно 0
, поэтому получаем setNumber(0 + 5)
. React добавляет в свою очередь «заменить на 5».
setNumber(n => n + 1)
: n => n + 1
— это функция обновления. React добавляет эту функцию в свою очередь.
Во время следующего рендера React проходит через очередь состояний:
запланированное обновление | n | возвращает |
---|
"заменить на 5" | 0 (не используется) | 5 |
n => n + 1 | 5 | 5 + 1 = 6 |
React сохраняет 6
в качестве конечного результата и возвращает его из useState
.
Вы могли заметить, что setState(x)
на самом деле работает как setState(n => x)
, но n
не используется.
Что произойдет, если вы замените состояние после его обновления
Давайте попробуем еще один пример. Как вы думаете, какое число будет в следующем рендере?
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
setNumber(42);
}}>
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button
onClick={() => {
setNumber(number + 5);
setNumber((n) => n + 1);
setNumber(42);
}}
>
Increase the number
</button>
</>
);
}
Вот как React работает с этими строками кода при выполнении этого обработчика событий:
setNumber(number + 5)
: number
равно 0
, поэтому setNumber(0 + 5)
. React добавляет в свою очередь «заменить на 5».
setNumber(n => n + 1)
: n => n + 1
— это функция обновления. React добавляет эту функцию в свою очередь.
setNumber(42)
: React добавляет в свою очередь «заменить на 42».
Во время следующего рендера React проходит через очередь состояний:
запланированное обновление | n | возвращает |
---|
"заменить на 5" | 0 (не используется) | 5 |
n => n + 1 | 5 | 5 + 1 = 6 |
"заменить на 42" | 6 (не используется) | 42 |
Затем React сохраняет 42
как окончательный результат и возвращает его из useState
.
Подводя итог, вот как вы можете думать о том, что вы передаете установщику состояния setNumber
:
- Функция обновления (например,
n => n + 1
) добавляется в очередь.
- Любое другое значение (например, число
5
) добавляет в очередь «заменить на 5», игнорируя то, что уже находится в очереди.
После завершения обработчика событий React запустит повторный рендеринг. Во время повторного рендеринга React обработает очередь. Функции обновления запускаются во время рендеринга, поэтому функции обновления должны быть чистыми и возвращать только результат. Не пытайтесь установить состояние внутри них или запустить другие побочные эффекты. В строгом режиме React дважды запускает каждую функцию обновления (но отбрасывает второй результат), чтобы помочь вам найти ошибки.
Соглашения об именах
Аргумент функции обновления принято называть первыми буквами соответствующей переменной состояния:
setEnabled((e) => !e);
setLastName((ln) => ln.reverse());
setFriendCount((fc) => fc * 2);
Если вы предпочитаете более подробный код, другим распространенным соглашением является повторение полного имени переменной состояния, например setEnabled(enabled => !enabled)
, или использование префикса, такого как setEnabled(prevEnabled => !prevEnabled)
.
Резюме
- Установка состояния не изменяет переменную в существующем рендеринге, но запрашивает новый рендеринг.
- React обрабатывает обновления состояния после завершения работы обработчиков событий. Это называется батчингом.
- Чтобы обновить некоторое состояние несколько раз в одном событии, вы можете использовать функцию обновления
setNumber(n => n + 1)
.