Научете как ефективно да категоризирате и обработвате грешки в React Error Boundaries, подобрявайки стабилността на приложението и потребителското изживяване.
Категоризиране на грешки в React Error Boundaries: Цялостно ръководство
Обработката на грешки е критичен аспект от изграждането на здрави и лесни за поддръжка React приложения. Докато Error Boundaries на React предоставят механизъм за плавна обработка на грешки, възникнали по време на рендиране, разбирането на това как да се категоризират и да се реагира на различните типове грешки е от решаващо значение за създаването на наистина устойчиво приложение. Това ръководство изследва различни подходи за категоризиране на грешки в рамките на Error Boundaries, като предлага практически примери и полезни съвети за подобряване на вашата стратегия за управление на грешки.
Какво представляват React Error Boundaries?
Въведени в React 16, Error Boundaries са React компоненти, които улавят JavaScript грешки навсякъде в дървото на своите дъщерни компоненти, записват тези грешки и показват резервен потребителски интерфейс (fallback UI), вместо да сриват цялото дърво от компоненти. Те функционират подобно на try...catch блок, но за компоненти.
Ключови характеристики на Error Boundaries:
- Обработка на грешки на ниво компонент: Изолирайте грешките в рамките на конкретни поддървета на компоненти.
- Плавно понижаване на функционалността: Предотвратете срив на цялото приложение поради грешка в един компонент.
- Контролиран резервен UI: Показвайте удобно за потребителя съобщение или алтернативно съдържание, когато възникне грешка.
- Записване на грешки (logging): Улеснете проследяването и отстраняването на грешки, като записвате информация за тях.
Защо да категоризираме грешките в Error Boundaries?
Простото улавяне на грешки не е достатъчно. Ефективната обработка на грешки изисква разбиране на какво се е объркало и съответна реакция. Категоризирането на грешки в Error Boundaries предлага няколко предимства:
- Целенасочена обработка на грешки: Различните типове грешки може да изискват различни реакции. Например мрежова грешка може да наложи механизъм за повторен опит, докато грешка при валидиране на данни може да изисква корекция на въведените от потребителя данни.
- Подобрено потребителско изживяване: Показвайте по-информативни съобщения за грешки въз основа на типа на грешката. Общо съобщение като „Нещо се обърка“ е по-малко полезно от конкретно съобщение, указващо проблем с мрежата или невалидни данни.
- Подобрено отстраняване на грешки: Категоризирането на грешките предоставя ценен контекст за отстраняване на грешки и идентифициране на основната причина за проблемите.
- Проактивно наблюдение: Проследявайте честотата на различните типове грешки, за да идентифицирате повтарящи се проблеми и да приоритизирате поправките.
- Стратегически резервен UI: Показвайте различни резервни потребителски интерфейси в зависимост от грешката, като предоставяте по-уместна информация или действия на потребителя.
Подходи за категоризиране на грешки
Могат да се използват няколко техники за категоризиране на грешки в React Error Boundaries:
1. Използване на instanceof
Операторът instanceof проверява дали даден обект е инстанция на определен клас. Това е полезно за категоризиране на грешки въз основа на техните вградени или персонализирани типове грешки.
Пример:
class NetworkError extends Error {
constructor(message) {
super(message);
this.name = "NetworkError";
}
}
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
class MyErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, errorInfo: null };
}
static getDerivedStateFromError(error) {
// Актуализирайте състоянието, така че следващото изобразяване да покаже резервния UI.
return { hasError: true, error: error };
}
componentDidCatch(error, errorInfo) {
// Можете също да запишете грешката в услуга за докладване на грешки
console.error("Caught error:", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
// Можете да изобразите всякакъв персонализиран резервен UI
let errorMessage = "Нещо се обърка.";
if (this.state.error instanceof NetworkError) {
errorMessage = "Възникна мрежова грешка. Моля, проверете връзката си и опитайте отново.";
} else if (this.state.error instanceof ValidationError) {
errorMessage = "Възникна грешка при валидацията. Моля, прегледайте въведените данни.";
}
return (
<div>
<h2>Грешка!</h2>
<p>{errorMessage}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
Обяснение:
- Дефинирани са персонализирани класове
NetworkErrorиValidationError, които разширяват вградения класError. - В метода
renderна компонентаMyErrorBoundaryсе използва операторътinstanceof, за да се провери типът на уловената грешка. - Въз основа на типа на грешката се показва конкретно съобщение за грешка в резервния UI.
2. Използване на кодове или свойства на грешките
Друг подход е да се включват кодове или свойства на грешките в самия обект на грешката. Това позволява по-фино категоризиране въз основа на конкретни сценарии на грешки.
Пример:
function fetchData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then(response => {
if (!response.ok) {
const error = new Error("Мрежовата заявка е неуспешна");
error.code = response.status; // Добавете персонализиран код за грешка
reject(error);
}
return response.json();
})
.then(data => resolve(data))
.catch(error => reject(error));
});
}
class MyErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, errorInfo: null };
}
static getDerivedStateFromError(error) {
// Актуализирайте състоянието, така че следващото изобразяване да покаже резервния UI.
return { hasError: true, error: error };
}
componentDidCatch(error, errorInfo) {
// Можете също да запишете грешката в услуга за докладване на грешки
console.error("Caught error:", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
let errorMessage = "Нещо се обърка.";
if (this.state.error.code === 404) {
errorMessage = "Ресурсът не е намерен.";
} else if (this.state.error.code >= 500) {
errorMessage = "Грешка в сървъра. Моля, опитайте отново по-късно.";
}
return (
<div>
<h2>Грешка!</h2>
<p>{errorMessage}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
Обяснение:
- Функцията
fetchDataдобавя свойствоcodeкъм обекта на грешката, което представлява HTTP статус кода. - Компонентът
MyErrorBoundaryпроверява свойствотоcode, за да определи конкретния сценарий на грешката. - Въз основа на кода на грешката се показват различни съобщения за грешки.
3. Използване на централизирано съпоставяне на грешки
За сложни приложения поддържането на централизирано съпоставяне на грешки може да подобри организацията и поддръжката на кода. Това включва създаване на речник или обект, който съпоставя типове или кодове на грешки с конкретни съобщения за грешки и логика за обработка.
Пример:
const errorMap = {
"NETWORK_ERROR": {
message: "Възникна мрежова грешка. Моля, проверете връзката си.",
retry: true,
},
"INVALID_INPUT": {
message: "Невалидни данни. Моля, прегледайте въведените данни.",
retry: false,
},
404: {
message: "Ресурсът не е намерен.",
retry: false,
},
500: {
message: "Грешка в сървъра. Моля, опитайте отново по-късно.",
retry: true,
},
"DEFAULT": {
message: "Нещо се обърка.",
retry: false,
},
};
function handleCustomError(errorType) {
const errorDetails = errorMap[errorType] || errorMap["DEFAULT"];
return errorDetails;
}
class MyErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, errorDetails: null, errorInfo: null };
}
static getDerivedStateFromError(error) {
// Актуализирайте състоянието, така че следващото изобразяване да покаже резервния UI.
const errorDetails = handleCustomError(error.message);
return { hasError: true, errorDetails: errorDetails };
}
componentDidCatch(error, errorInfo) {
// Можете също да запишете грешката в услуга за докладване на грешки
console.error("Caught error:", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
const { message } = this.state.errorDetails;
return (
<div>
<h2>Грешка!</h2>
<p>{message}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.errorDetails.message}<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
function MyComponent(){
const [data, setData] = React.useState(null);
React.useEffect(() => {
try {
throw new Error("NETWORK_ERROR");
} catch (e) {
throw e;
}
}, []);
return <div></div>;
}
Обяснение:
- Обектът
errorMapсъхранява информация за грешките, включително съобщения и флагове за повторен опит, въз основа на типове или кодове на грешки. - Функцията
handleCustomErrorизвлича подробности за грешката отerrorMapвъз основа на съобщението за грешка и връща стойности по подразбиране, ако не е намерен конкретен код. - Компонентът
MyErrorBoundaryизползваhandleCustomError, за да получи подходящото съобщение за грешка отerrorMap.
Добри практики за категоризиране на грешки
- Дефинирайте ясни типове грешки: Установете последователен набор от типове или кодове на грешки за вашето приложение.
- Предоставяйте контекстуална информация: Включвайте подходящи подробности в обектите на грешки, за да улесните отстраняването на грешки.
- Централизирайте логиката за обработка на грешки: Използвайте централизирано съпоставяне на грешки или помощни функции, за да управлявате обработката на грешки последователно.
- Записвайте грешките ефективно: Интегрирайте се с услуги за докладване на грешки, за да проследявате и анализирате грешки в продукционна среда. Популярни услуги включват Sentry, Rollbar и Bugsnag.
- Тествайте обработката на грешки: Пишете unit тестове, за да проверите дали вашите Error Boundaries правилно обработват различните типове грешки.
- Вземете предвид потребителското изживяване: Показвайте информативни и лесни за разбиране съобщения за грешки, които насочват потребителите към разрешаване на проблема. Избягвайте техническия жаргон.
- Наблюдавайте честотата на грешките: Проследявайте честотата на различните типове грешки, за да идентифицирате повтарящи се проблеми и да приоритизирате поправките.
- Интернационализация (i18n): Когато представяте съобщения за грешки на потребителя, уверете се, че вашите съобщения са правилно интернационализирани, за да поддържат различни езици и култури. Използвайте библиотеки като
i18nextили Context API на React, за да управлявате преводите. - Достъпност (a11y): Уверете се, че съобщенията ви за грешки са достъпни за потребители с увреждания. Използвайте ARIA атрибути, за да предоставите допълнителен контекст на екранните четци.
- Сигурност: Бъдете внимателни каква информация показвате в съобщенията за грешки, особено в продукционна среда. Избягвайте излагането на чувствителни данни, които биха могли да бъдат използвани от нападатели. Например не показвайте сурови stack traces на крайните потребители.
Примерен сценарий: Обработка на API грешки в приложение за електронна търговия
Разгледайте приложение за електронна търговия, което извлича информация за продукти от API. Потенциалните сценарии за грешки включват:
- Мрежови грешки: API сървърът не е достъпен или интернет връзката на потребителя е прекъсната.
- Грешки при удостоверяване: Токенът за удостоверяване на потребителя е невалиден или е изтекъл.
- Грешки "Ресурсът не е намерен": Исканият продукт не съществува.
- Сървърни грешки: API сървърът среща вътрешна грешка.
Използвайки Error Boundaries и категоризиране на грешки, приложението може да обработи тези сценарии плавно:
// Пример (опростен)
async function fetchProduct(productId) {
try {
const response = await fetch(`/api/products/${productId}`);
if (!response.ok) {
if (response.status === 404) {
throw new Error("PRODUCT_NOT_FOUND");
} else if (response.status === 401 || response.status === 403) {
throw new Error("AUTHENTICATION_ERROR");
} else {
throw new Error("SERVER_ERROR");
}
}
return await response.json();
} catch (error) {
if (error instanceof TypeError && error.message === "Failed to fetch") {
throw new Error("NETWORK_ERROR");
}
throw error;
}
}
class ProductErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, errorDetails: null, errorInfo: null };
}
static getDerivedStateFromError(error) {
const errorDetails = handleCustomError(error.message); // Използвайте errorMap, както е показано по-рано
return { hasError: true, errorDetails: errorDetails };
}
componentDidCatch(error, errorInfo) {
console.error("Caught error:", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
const { message, retry } = this.state.errorDetails;
return (
<div>
<h2>Грешка!</h2>
<p>{message}</p>
{retry && <button onClick={() => window.location.reload()}>Опитай отново</button>}
</div>
);
}
return this.props.children;
}
}
Обяснение:
- Функцията
fetchProductпроверява кода на отговора от API и хвърля специфични типове грешки въз основа на статуса. - Компонентът
ProductErrorBoundaryулавя тези грешки и показва подходящи съобщения за грешки. - При мрежови и сървърни грешки се показва бутон „Опитай отново“, който позволява на потребителя да опита заявката отново.
- При грешки в удостоверяването потребителят може да бъде пренасочен към страницата за вход.
- При грешки „ресурсът не е намерен“ се показва съобщение, че продуктът не съществува.
Заключение
Категоризирането на грешки в React Error Boundaries е от съществено значение за изграждането на устойчиви и лесни за ползване приложения. Чрез използването на техники като проверки с instanceof, кодове на грешки и централизирани съпоставяния на грешки, можете ефективно да обработвате различни сценарии на грешки и да предоставите по-добро потребителско изживяване. Не забравяйте да следвате най-добрите практики за обработка, записване и тестване на грешки, за да гарантирате, че вашето приложение се справя плавно с неочаквани ситуации.
Чрез прилагането на тези стратегии можете значително да подобрите стабилността и поддръжката на вашите React приложения, осигурявайки по-гладко и по-надеждно изживяване за вашите потребители, независимо от тяхното местоположение или произход.
Допълнителни ресурси: