<StrictMode>
<StrictMode> позволяет обнаружить распространённые ошибки в ваших компонентах на ранней стадии разработки.
<StrictMode>
<App />
</StrictMode>
- Ссылка
-
Использование
- Включение режима строгости для всего приложения
- Включение режима строгости для части приложения
- Исправление ошибок, обнаруженных при двойном рендеринге в режиме разработки
- Исправление ошибок, обнаруженных при повторном выполнении эффектов в режиме разработки
- Исправление ошибок, обнаруженных при повторном выполнении обратных вызовов ref в режиме разработки
- Исправление предупреждений об устаревших API, включённых режимом строгости
Ссылка
<StrictMode>
Используйте StrictMode для активации дополнительных поведенческих особенностей и предупреждений для дерева компонентов внутри:
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<App />
</StrictMode>
); Режим строгости включает следующие поведенческие особенности, действующие только в режиме разработки:
- Ваши компоненты будут перерендериться ещё раз для выявления ошибок, вызванных нечистым рендерингом.
- Ваши компоненты будут повторно выполнять эффекты ещё раз для выявления ошибок, вызванных отсутствием очистки эффектов.
- Ваши компоненты будут повторно выполнять обратные вызовы ref ещё раз для выявления ошибок, вызванных отсутствием очистки ref.
- Ваши компоненты будут проверяться на использование устаревших API.
Свойства
StrictMode не принимает свойства.
Ограничения
- Нет способа отказаться от режима строгости внутри дерева, обернутого в
<StrictMode>. Это даёт уверенность в том, что все компоненты внутри<StrictMode>проверяются. Если две команды, работающие над продуктом, не согласны по поводу того, ценны ли проверки, они должны либо достичь консенсуса, либо опустить<StrictMode>ниже в дереве.
Использование
Включение режима строгости для всего приложения
Режим строгости включает дополнительные проверки, действующие только в режиме разработки, для всего дерева компонентов внутри компонента <StrictMode>. Эти проверки помогают обнаружить распространённые ошибки в ваших компонентах на ранних этапах разработки.
Чтобы включить режим строгости для всего приложения, оберните ваш корневой компонент в <StrictMode> при рендеринге:
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<App />
</StrictMode>
); Рекомендуется обернуть всё ваше приложение в режим строгости, особенно для вновь созданных приложений. Если вы используете фреймворк, который вызывает createRoot за вас, ознакомьтесь с его документацией, чтобы узнать, как включить режим строгости.
Хотя проверки режима строгости выполняются только в режиме разработки, они помогают обнаружить ошибки, которые уже существуют в вашем коде, но могут быть сложными для воспроизведения в продакшене. Режим строгости позволяет исправить ошибки до того, как пользователи о них сообщат.
Примечание
Режим строгости включает следующие проверки в режиме разработки:
- Ваши компоненты будут перерендериться ещё раз для выявления ошибок, вызванных нечистым рендерингом.
- Ваши компоненты будут повторно выполнять эффекты ещё раз для выявления ошибок, вызванных отсутствием очистки эффектов.
- Ваши компоненты будут повторно выполнять обратные вызовы ref ещё раз для выявления ошибок, вызванных отсутствием очистки ref.
- Ваши компоненты будут проверяться на использование устаревших API.
Все эти проверки выполняются только в режиме разработки и не влияют на сборку продакшен-версии.
Включение режима строгости для части приложения
Вы также можете включить режим строгости для любой части вашего приложения:
import { StrictMode } from 'react';
function App() {
return (
<>
<Header />
<StrictMode>
<main>
<Sidebar />
<Content />
</main>
</StrictMode>
<Footer />
</>
);
} В этом примере проверки режима строгости не будут выполняться для компонентов Header и Footer. Однако они будут выполняться для компонентов Sidebar и Content, а также для всех компонентов внутри них, независимо от уровня вложенности.
Исправление ошибок, обнаруженных при двойном рендеринге в режиме разработки
React предполагает, что каждый создаваемый вами компонент — чистая функция. Это означает, что ваши React-компоненты должны всегда возвращать одинаковый JSX при одинаковых входных данных (свойства, состояние и контекст).
Компоненты, нарушающие это правило, ведут себя непредсказуемо и вызывают ошибки. Чтобы помочь вам найти случайный нечистый код, режим строгости вызывает некоторые из ваших функций (только те, которые должны быть чистыми) дважды в режиме разработки. Это включает:
- Тело функции вашего компонента (только логика верхнего уровня, поэтому это не включает код внутри обработчиков событий)
- Функции, которые вы передаёте в
useState,setфункции,useMemoилиuseReducer - Некоторые методы компонентов класса, такие как
constructor,render,shouldComponentUpdate(см. весь список)
Если функция чистая, её повторное выполнение не изменяет её поведения, потому что чистая функция всегда возвращает один и тот же результат. Однако если функция нечистая (например, она изменяет данные, которые она получает), её повторное выполнение, как правило, заметно (это делает её нечистой!). Это помогает вам обнаружить и исправить ошибку на ранней стадии.
Вот пример, иллюстрирующий, как двойной рендеринг в режиме строгости помогает вам найти ошибки на ранней стадии.
Этот StoryTray компонент принимает массив stories и добавляет ещё один элемент «Создать историю» в конец:
export default function StoryTray({ stories }) { const items = stories; items.push({ id: 'create', label: 'Create Story' }); return ( <ul> {items.map(story => ( <li key={story.id}> {story.label} </li> ))} </ul> ); }
В коде выше есть ошибка. Однако её легко пропустить, так как начальный результат кажется правильным.
Эта ошибка станет более заметной, если компонент StoryTray будет перерендериться несколько раз. Например, давайте заставим компонент StoryTray перерендериться с другим фоновым цветом при наведении на него мышью:
import { useState } from 'react'; export default function StoryTray({ stories }) { const [isHover, setIsHover] = useState(false); const items = stories; items.push({ id: 'create', label: 'Create Story' }); return ( <ul onPointerEnter={() => setIsHover(true)} onPointerLeave={() => setIsHover(false)} style={{ backgroundColor: isHover ? '#ddd' : '#fff' }} > {items.map(story => ( <li key={story.id}> {story.label} </li> ))} </ul> ); }
Заметьте, как каждый раз при наведении на компонент StoryTray «Создать историю» добавляется в список снова. Цель кода была добавить его один раз в конец. Но StoryTray напрямую изменяет массив stories из свойств. Каждый раз, когда StoryTray перерендеривается, он добавляет «Создать историю» снова в конец того же массива. Другими словами, StoryTray — это не чистая функция — её повторное выполнение приводит к разным результатам.
Чтобы исправить эту проблему, можно сделать копию массива и изменить эту копию вместо оригинального:
export default function StoryTray({ stories }) {
const items = stories.slice(); // Clone the array
// ✅ Good: Pushing into a new array
items.push({ id: 'create', label: 'Create Story' }); Это сделает функцию StoryTray чистой. Каждый раз при её вызове она будет изменять только новую копию массива и не будет влиять на внешние объекты или переменные. Это решает ошибку, но вам пришлось заставить компонент перерендериться чаще, прежде чем стало очевидно, что в его поведении есть что-то не так.
Теперь обернём исходный (ошибочный) код в <StrictMode>:
export default function StoryTray({ stories }) { const items = stories; items.push({ id: 'create', label: 'Create Story' }); return ( <ul> {items.map(story => ( <li key={story.id}> {story.label} </li> ))} </ul> ); }
Режим строгости всегда вызывает функцию рендеринга дважды, поэтому вы сразу увидите ошибку («Создать историю» появляется дважды). Это позволяет вам заметить такие ошибки на ранней стадии процесса. Когда вы исправьте свой компонент так, чтобы он рендерился в режиме строгости, вы также исправите многие потенциальные будущие ошибки в продакшене, такие как функциональность наведения мышью, о которой говорилось ранее:
import { useState } from 'react'; export default function StoryTray({ stories }) { const [isHover, setIsHover] = useState(false); const items = stories.slice(); // Clone the array items.push({ id: 'create', label: 'Create Story' }); return ( <ul onPointerEnter={() => setIsHover(true)} onPointerLeave={() => setIsHover(false)} style={{ backgroundColor: isHover ? '#ddd' : '#fff' }} > {items.map(story => ( <li key={story.id}> {story.label} </li> ))} </ul> ); }
Без режима строгости ошибку было легко пропустить, пока вы не добавили больше перерендерингов. Режим строгости сразу выявил ту же ошибку. Режим строгости помогает вам обнаружить ошибки до того, как вы их передадите своей команде и своим пользователям.
Подробнее о том, как сделать компоненты чистыми.
Примечание
Если у вас установлен React DevTools, все вызовы console.log во время второго вызова рендеринга будут отображаться слегка приглушёнными. React DevTools также предлагает настройку (по умолчанию выключена) для полного подавления этих вызовов.
Исправление ошибок, обнаруженных при повторном выполнении эффектов в режиме разработки
Режим строгости также может помочь в поиске ошибок в эффектах.
Каждый эффект имеет код настройки и, возможно, код очистки. Обычно React вызывает настройку при монтировании (добавлении на экран) компонента и вызывает очистку при размонтировании (удалении с экрана) компонента. Затем React повторно вызывает очистку и настройку, если зависящие от них данные изменились с момента последнего рендеринга.
Когда режим строгости включён, React также выполнит один дополнительный цикл настройки+очистки в режиме разработки для каждого эффекта. Это может показаться неожиданным, но это помогает выявить скрытые ошибки, которые трудно поймать вручную.
Вот пример, иллюстрирующий, как повторное выполнение эффектов в режиме строгости помогает вам найти ошибки на ранней стадии.
Рассмотрим этот пример, который подключает компонент к чату:
import { createRoot } from 'react-dom/client'; import './styles.css'; import App from './App'; const root = createRoot(document.getElementById("root")); root.render(<App />);
В этом коде есть проблема, но она может не быть сразу очевидной.
Чтобы сделать проблему более очевидной, давайте реализуем функцию. В примере ниже roomId не является жёстко заданным. Вместо этого пользователь может выбрать roomId, к которому он хочет подключиться, из раскрывающегося списка. Нажмите «Открыть чат», а затем выбирайте разные чаты один за другим. Следите за количеством активных подключений в консоли:
import { createRoot } from 'react-dom/client'; import './styles.css'; import App from './App'; const root = createRoot(document.getElementById("root")); root.render(<App />);
Вы заметите, что количество открытых подключений постоянно растёт. В реальном приложении это приведёт к проблемам с производительностью и сетью. Проблема в том, что вашему эффекту не хватает функции очистки:
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId]); Теперь, когда ваш эффект «очищает» себя после себя и уничтожает устаревшие подключения, утечка решена. Однако обратите внимание, что проблема не стала видимой до тех пор, пока вы не добавили больше функций (выпадающий список).
Теперь обернём исходный (ошибочный) код в <StrictMode>:
import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import './styles.css'; import App from './App'; const root = createRoot(document.getElementById("root")); root.render( <StrictMode> <App /> </StrictMode> );
При использовании строгого режима, вы сразу увидите проблему (количество активных подключений увеличивается до 2). Строгий режим выполняет дополнительный цикл настройки + очистки для каждого эффекта. Этот эффект не имеет логики очистки, поэтому он создаёт дополнительное подключение, но не уничтожает его. Это подсказка о том, что вам не хватает функции очистки.
Строгий режим позволяет обнаружить такие ошибки на ранней стадии. Когда вы исправите свой эффект, добавив функцию очистки в строгом режиме, вы также исправите множество потенциальных ошибок в работе приложения, таких как проблема с раскрывающимся списком, упомянутая ранее:
import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import './styles.css'; import App from './App'; const root = createRoot(document.getElementById("root")); root.render( <StrictMode> <App /> </StrictMode> );
Обратите внимание, как количество активных подключений в консоли больше не увеличивается.
Без строгого режима легко было пропустить тот факт, что вашему эффекту нужна очистка. Выполняя настройку → очистку → настройку вместо настройки для вашего эффекта в разработке, режим строгости сделал недостающую логику очистки более заметной.
Подробнее об реализации очистки эффекта.
Исправление ошибок, обнаруженных при повторном запуске обратных вызовов ref в разработке
Строгий режим также может помочь найти ошибки в обратных вызовах refs.
Каждый обратный вызов ref имеет код настройки и, возможно, код очистки. Обычно React вызывает настройку, когда элемент создаётся (добавляется в DOM), и вызывает очистку, когда элемент удаляется (удаляется из DOM).
При включённом строгом режиме React также выполнит один дополнительный цикл настройки + очистки в разработке для каждого обратного вызова ref. Это может показаться неожиданным, но это помогает выявить скрытые ошибки, которые сложно поймать вручную.
Рассмотрим этот пример, который позволяет выбрать животное и затем прокрутить к одному из них. Обратите внимание, когда вы переключаетесь с «Кошки» на «Собаки», в консоли отображается, что количество животных в списке постоянно растёт, а кнопки «Прокрутить к» перестают работать:
import { useRef, useState } from "react"; export default function AnimalFriends() { const itemsRef = useRef([]); const [animalList, setAnimalList] = useState(setupAnimalList); const [animal, setAnimal] = useState('cat'); function scrollToAnimal(index) { const list = itemsRef.current; const {node} = list[index]; node.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center", }); } const animals = animalList.filter(a => a.type === animal) return ( <> <nav> <button onClick={() => setAnimal('cat')}>Cats</button> <button onClick={() => setAnimal('dog')}>Dogs</button> </nav> <hr /> <nav> <span>Scroll to:</span>{animals.map((animal, index) => ( <button key={animal.src} onClick={() => scrollToAnimal(index)}> {index} </button> ))} </nav> <div> <ul> {animals.map((animal) => ( <li key={animal.src} ref={(node) => { const list = itemsRef.current; const item = {animal: animal, node}; list.push(item); console.log(`✅ Adding animal to the map. Total animals: ${list.length}`); if (list.length > 10) { console.log('❌ Too many animals in the list!'); } return () => { // 🚩 No cleanup, this is a bug! } }} > <img src={animal.src} /> </li> ))} </ul> </div> </> ); } function setupAnimalList() { const animalList = []; for (let i = 0; i < 10; i++) { animalList.push({type: 'cat', src: "https://loremflickr.com/320/240/cat?lock=" + i}); } for (let i = 0; i < 10; i++) { animalList.push({type: 'dog', src: "https://loremflickr.com/320/240/dog?lock=" + i}); } return animalList; }
Это ошибка в работе приложения! Поскольку обратный вызов ref не удаляет животных из списка в процессе очистки, список животных постоянно растёт. Это утечка памяти, которая может вызвать проблемы с производительностью в реальном приложении и нарушает поведение приложения.
Проблема в том, что обратный вызов ref не очищается после себя:
<li
ref={node => {
const list = itemsRef.current;
const item = {animal, node};
list.push(item);
return () => {
// 🚩 No cleanup, this is a bug!
}
}}
</li> Теперь обернём исходный (ошибочный) код в <StrictMode>.
import { useRef, useState } from "react"; export default function AnimalFriends() { const itemsRef = useRef([]); const [animalList, setAnimalList] = useState(setupAnimalList); const [animal, setAnimal] = useState('cat'); function scrollToAnimal(index) { const list = itemsRef.current; const {node} = list[index]; node.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center", }); } const animals = animalList.filter(a => a.type === animal) return ( <> <nav> <button onClick={() => setAnimal('cat')}>Cats</button> <button onClick={() => setAnimal('dog')}>Dogs</button> </nav> <hr /> <nav> <span>Scroll to:</span>{animals.map((animal, index) => ( <button key={animal.src} onClick={() => scrollToAnimal(index)}> {index} </button> ))} </nav> <div> <ul> {animals.map((animal) => ( <li key={animal.src} ref={(node) => { const list = itemsRef.current; const item = {animal: animal, node} list.push(item); console.log(`✅ Adding animal to the map. Total animals: ${list.length}`); if (list.length > 10) { console.log('❌ Too many animals in the list!'); } return () => { // 🚩 No cleanup, this is a bug! } }} > <img src={animal.src} /> </li> ))} </ul> </div> </> ); } function setupAnimalList() { const animalList = []; for (let i = 0; i < 10; i++) { animalList.push({type: 'cat', src: "https://loremflickr.com/320/240/cat?lock=" + i}); } for (let i = 0; i < 10; i++) { animalList.push({type: 'dog', src: "https://loremflickr.com/320/240/dog?lock=" + i}); } return animalList; }
При использовании строгого режима вы сразу увидите проблему. Строгий режим выполняет дополнительный цикл настройки + очистки для каждого обратного вызова ref. Этот обратный вызов ref не имеет логики очистки, поэтому он добавляет refs, но не удаляет их. Это подсказка о том, что вам не хватает функции очистки.
Строгий режим позволяет быстро находить ошибки в обратных вызовах ref. Когда вы исправите свой обратный вызов, добавив функцию очистки в строгом режиме, вы также исправите множество потенциальных ошибок в работе приложения, таких как ошибка с «Прокруткой к» из предыдущего примера:
import { useRef, useState } from "react"; export default function AnimalFriends() { const itemsRef = useRef([]); const [animalList, setAnimalList] = useState(setupAnimalList); const [animal, setAnimal] = useState('cat'); function scrollToAnimal(index) { const list = itemsRef.current; const {node} = list[index]; node.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center", }); } const animals = animalList.filter(a => a.type === animal) return ( <> <nav> <button onClick={() => setAnimal('cat')}>Cats</button> <button onClick={() => setAnimal('dog')}>Dogs</button> </nav> <hr /> <nav> <span>Scroll to:</span>{animals.map((animal, index) => ( <button key={animal.src} onClick={() => scrollToAnimal(index)}> {index} </button> ))} </nav> <div> <ul> {animals.map((animal) => ( <li key={animal.src} ref={(node) => { const list = itemsRef.current; const item = {animal, node}; list.push({animal: animal, node}); console.log(`✅ Adding animal to the map. Total animals: ${list.length}`); if (list.length > 10) { console.log('❌ Too many animals in the list!'); } return () => { list.splice(list.indexOf(item)); console.log(`❌ Removing animal from the map. Total animals: ${itemsRef.current.length}`); } }} > <img src={animal.src} /> </li> ))} </ul> </div> </> ); } function setupAnimalList() { const animalList = []; for (let i = 0; i < 10; i++) { animalList.push({type: 'cat', src: "https://loremflickr.com/320/240/cat?lock=" + i}); } for (let i = 0; i < 10; i++) { animalList.push({type: 'dog', src: "https://loremflickr.com/320/240/dog?lock=" + i}); } return animalList; }
Теперь при первоначальной установке в режиме строгости обратные вызовы ref проходят все этапы настройки, очистки и повторной настройки:
...
✅ Adding animal to the map. Total animals: 10
...
❌ Removing animal from the map. Total animals: 0
...
✅ Adding animal to the map. Total animals: 10 Это ожидаемо. Строгий режим подтверждает, что обратные вызовы ref очищаются корректно, поэтому размер никогда не превышает ожидаемого значения. После исправления нет утечек памяти, и все функции работают как ожидается.
Без строгого режима было легко пропустить ошибку, пока вы не нажимали по различным элементам приложения, чтобы заметить, что некоторые функции не работают. Строгий режим сразу показал ошибки, прежде чем вы выпустили приложение в работу.
Исправление предупреждений об устаревании, включённых в строгий режим
React предупреждает, если какой-либо компонент внутри <StrictMode> дерева использует устаревший API:
-
UNSAFE_методы жизненного цикла класса, такие какUNSAFE_componentWillMount. См. альтернативы.
Эти API в основном используются в старых классовых компонентах, поэтому они редко встречаются в современных приложениях.
© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://react.dev/reference/react/StrictMode