Įvaldykite React klaidų ribas (Error Boundaries), kad kurtumėte atsparias ir patogias vartotojui programas. Sužinokite geriausias praktikas, diegimo būdus ir pažangias klaidų valdymo strategijas.
React klaidų ribos (Error Boundaries): Elegantiški klaidų valdymo būdai patikimoms programoms
Dinamiškame svetainių kūrimo pasaulyje ypač svarbu kurti patikimas ir vartotojui patogias programas. React, populiari JavaScript biblioteka, skirta vartotojo sąsajoms kurti, siūlo galingą mechanizmą elegantiškam klaidų valdymui: klaidų ribas (Error Boundaries). Šis išsamus vadovas gilinsis į klaidų ribų koncepciją, nagrinės jų paskirtį, diegimą ir geriausias praktikas kuriant atsparias React programas.
Klaidų ribų (Error Boundaries) poreikio supratimas
React komponentai, kaip ir bet koks kodas, yra pažeidžiami klaidų. Šios klaidos gali kilti iš įvairių šaltinių, įskaitant:
- Netikėti duomenys: Komponentai gali gauti duomenis netikėtu formatu, o tai sukelia atvaizdavimo problemų.
- Logikos klaidos: Klaidos komponento logikoje gali sukelti netikėtą elgesį ir klaidas.
- Išorinės priklausomybės: Problemos su išorinėmis bibliotekomis ar API gali perkelti klaidas į jūsų komponentus.
Be tinkamo klaidų valdymo, klaida React komponente gali sugadinti visą programą, o tai lemia prastą vartotojo patirtį. Klaidų ribos (Error Boundaries) suteikia būdą pagauti šias klaidas ir neleisti joms plisti aukštyn komponentų medyje, užtikrinant, kad programa išliktų funkcionali net ir sugedus atskiriems komponentams.
Kas yra React klaidų ribos (Error Boundaries)?
Klaidų ribos (Error Boundaries) yra React komponentai, kurie pagauna JavaScript klaidas bet kurioje savo vaikinių komponentų medžio vietoje, registruoja tas klaidas ir rodo atsarginę vartotojo sąsają (fallback UI) vietoje sugriuvusio komponentų medžio. Jie veikia kaip apsauginis tinklas, neleidžiantis klaidoms sugadinti visos programos.
Pagrindinės klaidų ribų savybės:
- Tik klasių komponentai: Klaidų ribos privalo būti įgyvendintos kaip klasių komponentai. Funkciniai komponentai ir „hooks“ negali būti naudojami klaidų riboms kurti.
- Gyvavimo ciklo metodai: Jie naudoja specifinius gyvavimo ciklo metodus,
static getDerivedStateFromError()
ircomponentDidCatch()
, klaidoms valdyti. - Lokalus klaidų valdymas: Klaidų ribos gaudo klaidas tik savo vaikiniuose komponentuose, o ne pačiose savyje.
Klaidų ribų diegimas
Panagrinėkime pagrindinio klaidų ribos (Error Boundary) komponento kūrimo procesą:
1. Klaidų ribos (Error Boundary) komponento kūrimas
Pirmiausia, sukurkite naują klasių komponentą, pavyzdžiui, pavadintą ErrorBoundary
:
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false
};
}
static getDerivedStateFromError(error) {
// Atnaujiname būseną, kad kitas atvaizdavimas parodytų atsarginę vartotojo sąsają.
return {
hasError: true
};
}
componentDidCatch(error, errorInfo) {
// Taip pat galite registruoti klaidą klaidų pranešimų tarnyboje
console.error("Caught error: ", error, errorInfo);
// Pavyzdys: logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// Galite atvaizduoti bet kokią pasirinktinę atsarginę vartotojo sąsają
return (
<div>
<h2>Kažkas nutiko negerai.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Paaiškinimas:
- Konstruktorius: Inicializuoja komponento būseną su
hasError: false
. static getDerivedStateFromError(error)
: Šis gyvavimo ciklo metodas iškviečiamas po to, kai vaikinio komponento išmesta klaida. Jis gauna klaidą kaip argumentą ir leidžia atnaujinti komponento būseną. Čia mes nustatomehasError
įtrue
, kad būtų paleista atsarginė vartotojo sąsaja. Tai yrastatic
metodas, todėl negalite naudotithis
funkcijos viduje.componentDidCatch(error, errorInfo)
: Šis gyvavimo ciklo metodas iškviečiamas po to, kai vaikinio komponento išmesta klaida. Jis gauna du argumentus:error
: Išmesta klaida.errorInfo
: Objektas, kuriame yra informacija apie komponentų dėklą (component stack), kur įvyko klaida. Tai neįkainojama derinant kodą.
Šiame metode galite registruoti klaidą tarnyboje, tokioje kaip „Sentry“, „Rollbar“ ar pasirinktiniame registravimo sprendime. Venkite bandyti iš naujo atvaizduoti ar taisyti klaidą tiesiogiai šioje funkcijoje; jos pagrindinis tikslas yra užregistruoti problemą.
render()
: Atvaizdavimo metodas tikrinahasError
būseną. Jei ji yratrue
, jis atvaizduoja atsarginę vartotojo sąsają (šiuo atveju, paprastą klaidos pranešimą). Kitu atveju, jis atvaizduoja komponento vaikus.
2. Klaidų ribos naudojimas
Norėdami naudoti klaidų ribą, tiesiog apgaubkite bet kurį komponentą, kuris gali išmesti klaidą, su ErrorBoundary
komponentu:
import ErrorBoundary from './ErrorBoundary';
function MyComponent() {
// Šis komponentas gali išmesti klaidą
return (
<ErrorBoundary>
<PotentiallyBreakingComponent />
</ErrorBoundary>
);
}
export default MyComponent;
Jei PotentiallyBreakingComponent
išmes klaidą, ErrorBoundary
ją pagaus, užregistruos klaidą ir atvaizduos atsarginę vartotojo sąsają.
3. Iliustratyvūs pavyzdžiai bendrame kontekste
Įsivaizduokite el. prekybos programą, rodančią produkto informaciją, gautą iš nuotolinio serverio. Komponentas ProductDisplay
yra atsakingas už produkto detalių atvaizdavimą. Tačiau serveris kartais gali grąžinti netikėtus duomenis, dėl ko kyla atvaizdavimo klaidų.
// ProductDisplay.js
import React from 'react';
function ProductDisplay({ product }) {
// Imituojame galimą klaidą, jei product.price nėra skaičius
if (typeof product.price !== 'number') {
throw new Error('Neteisinga produkto kaina');
}
return (
<div>
<h2>{product.name}</h2>
<p>Kaina: {product.price}</p>
<img src={product.imageUrl} alt={product.name} />
</div>
);
}
export default ProductDisplay;
Norėdami apsisaugoti nuo tokių klaidų, apgaubkite ProductDisplay
komponentą su ErrorBoundary
:
// App.js
import React from 'react';
import ErrorBoundary from './ErrorBoundary';
import ProductDisplay from './ProductDisplay';
function App() {
const product = {
name: 'Pavyzdinis produktas',
price: 'Ne skaičius', // Tyčia neteisingi duomenys
imageUrl: 'https://example.com/image.jpg'
};
return (
<div>
<ErrorBoundary>
<ProductDisplay product={product} />
</ErrorBoundary>
</div>
);
}
export default App;
Šiame scenarijuje, kadangi product.price
yra tyčia nustatyta kaip eilutė, o ne skaičius, ProductDisplay
komponentas išmes klaidą. ErrorBoundary
pagaus šią klaidą, neleisdamas visai programai sugriūti, ir vietoj neveikiančio ProductDisplay
komponento parodys atsarginę vartotojo sąsają.
4. Klaidų ribos internacionalizuotose programose
Kuriant programas pasaulinei auditorijai, klaidų pranešimai turėtų būti lokalizuoti, kad būtų užtikrinta geresnė vartotojo patirtis. Klaidų ribos gali būti naudojamos kartu su internacionalizacijos (i18n) bibliotekomis, norint rodyti išverstus klaidų pranešimus.
// ErrorBoundary.js (su i18n palaikymu)
import React from 'react';
import { useTranslation } from 'react-i18next'; // Darome prielaidą, kad naudojate react-i18next
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null,
};
}
static getDerivedStateFromError(error) {
return {
hasError: true,
error: error,
};
}
componentDidCatch(error, errorInfo) {
console.error("Caught error: ", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
return (
<FallbackUI error={this.state.error} errorInfo={this.state.errorInfo}/>
);
}
return this.props.children;
}
}
const FallbackUI = ({error, errorInfo}) => {
const { t } = useTranslation();
return (
<div>
<h2>{t('error.title')}</h2>
<p>{t('error.message')}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{error && error.toString()}<br />
{errorInfo?.componentStack}
</details>
</div>
);
}
export default ErrorBoundary;
Šiame pavyzdyje mes naudojame react-i18next
, kad išverstume klaidos pavadinimą ir pranešimą atsarginėje vartotojo sąsajoje. Funkcijos t('error.title')
ir t('error.message')
gaus atitinkamus vertimus pagal vartotojo pasirinktą kalbą.
5. Aspektai, susiję su serverio pusės atvaizdavimu (SSR)
Naudojant klaidų ribas serverio pusėje atvaizduojamose programose, labai svarbu tinkamai tvarkyti klaidas, kad serveris nenustotų veikti. React dokumentacijoje rekomenduojama vengti naudoti klaidų ribas atvaizdavimo klaidoms serveryje taisyti. Vietoj to, tvarkykite klaidas prieš atvaizduodami komponentą arba atvaizduokite statinį klaidos puslapį serveryje.
Geriausios klaidų ribų naudojimo praktikos
- Apgaubkite smulkius komponentus: Apgaubkite atskirus komponentus ar mažas programos dalis klaidų ribomis. Tai apsaugo nuo to, kad viena klaida sugadintų visą vartotojo sąsają. Apsvarstykite galimybę apgaubti konkrečias funkcijas ar modulius, o ne visą programą.
- Registruokite klaidas: Naudokite
componentDidCatch()
metodą klaidoms registruoti stebėjimo tarnyboje. Tai padeda sekti ir taisyti problemas jūsų programoje. Tokios tarnybos kaip „Sentry“, „Rollbar“ ir „Bugsnag“ yra populiarūs pasirinkimai klaidų sekimui ir pranešimams. - Pateikite informatyvią atsarginę vartotojo sąsają: Atsarginėje vartotojo sąsajoje rodykite vartotojui draugišką klaidos pranešimą. Venkite techninio žargono ir pateikite instrukcijas, kaip elgtis toliau (pvz., atnaujinti puslapį, susisiekti su palaikymo komanda). Jei įmanoma, pasiūlykite alternatyvius veiksmus, kuriuos vartotojas gali atlikti.
- Nepiktnaudžiaukite: Venkite apgaubti kiekvieną komponentą klaidų riba. Sutelkite dėmesį į sritis, kuriose klaidos yra labiau tikėtinos, pavyzdžiui, komponentus, kurie gauna duomenis iš išorinių API arba tvarko sudėtingas vartotojo sąveikas.
- Testuokite klaidų ribas: Įsitikinkite, kad jūsų klaidų ribos veikia teisingai, tyčia išmesdami klaidas komponentuose, kuriuos jos apgaubia. Rašykite vienetų (unit) ar integracijos testus, kad patikrintumėte, ar atsarginė vartotojo sąsaja rodoma kaip tikėtasi ir ar klaidos registruojamos teisingai.
- Klaidų ribos NĖRA skirtos:
- Įvykių doroklėms (event handlers)
- Asinchroniniam kodui (pvz.,
setTimeout
arrequestAnimationFrame
atgalinio iškvietimo funkcijoms) - Serverio pusės atvaizdavimui
- Klaidoms, išmestoms pačioje klaidų riboje (o ne jos vaikiniuose komponentuose)
Pažangios klaidų valdymo strategijos
1. Pakartotinio bandymo mechanizmai
Kai kuriais atvejais gali būti įmanoma atsigauti po klaidos pakartojant operaciją, kuri ją sukėlė. Pavyzdžiui, jei tinklo užklausa nepavyksta, galite ją pakartoti po trumpos pauzės. Klaidų ribos gali būti derinamos su pakartotinio bandymo mechanizmais, siekiant suteikti atsparesnę vartotojo patirtį.
// ErrorBoundaryWithRetry.js
import React from 'react';
class ErrorBoundaryWithRetry extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
retryCount: 0,
};
}
static getDerivedStateFromError(error) {
return {
hasError: true,
};
}
componentDidCatch(error, errorInfo) {
console.error("Caught error: ", error, errorInfo);
}
handleRetry = () => {
this.setState(prevState => ({
hasError: false,
retryCount: prevState.retryCount + 1,
}), () => {
// Tai priverčia komponentą persikrauti. Apsvarstykite geresnius šablonus su valdomais „props“.
this.forceUpdate(); // ĮSPĖJIMAS: Naudokite atsargiai
if (this.props.onRetry) {
this.props.onRetry();
}
});
};
render() {
if (this.state.hasError) {
return (
<div>
<h2>Kažkas nutiko negerai.</h2>
<button onClick={this.handleRetry}>Bandyti dar kartą</button>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundaryWithRetry;
Komponentas ErrorBoundaryWithRetry
turi pakartotinio bandymo mygtuką, kuris, jį paspaudus, atstato hasError
būseną ir iš naujo atvaizduoja vaikinius komponentus. Taip pat galite pridėti retryCount
, kad apribotumėte bandymų skaičių. Šis metodas gali būti ypač naudingas tvarkant laikinas klaidas, pvz., laikinus tinklo sutrikimus. Įsitikinkite, kad onRetry
savybė (prop) yra tinkamai apdorojama ir iš naujo gauna duomenis/vykdo logiką, kuri galėjo sukelti klaidą.
2. Funkcionalumo vėliavėlės (Feature Flags)
Funkcionalumo vėliavėlės leidžia dinamiškai įjungti arba išjungti funkcijas jūsų programoje, nediegiant naujo kodo. Klaidų ribos gali būti naudojamos kartu su funkcionalumo vėliavėlėmis, siekiant laipsniškai sumažinti funkcionalumą klaidos atveju. Pavyzdžiui, jei tam tikra funkcija sukelia klaidų, galite ją išjungti naudodami funkcionalumo vėliavėlę ir parodyti vartotojui pranešimą, nurodantį, kad funkcija laikinai nepasiekiama.
3. Grandinės pertraukiklio šablonas (Circuit Breaker Pattern)
Grandinės pertraukiklio šablonas yra programinės įrangos projektavimo šablonas, naudojamas siekiant išvengti, kad programa nuolat bandytų vykdyti operaciją, kuri greičiausiai nepavyks. Jis veikia stebėdamas operacijos sėkmės ir nesėkmės rodiklius ir, jei nesėkmių lygis viršija tam tikrą ribą, „atidaro grandinę“ ir tam tikrą laiką neleidžia toliau bandyti vykdyti operacijos. Tai gali padėti išvengti kaskadinių gedimų ir pagerinti bendrą programos stabilumą.
Klaidų ribos gali būti naudojamos grandinės pertraukiklio šablonui įgyvendinti React programose. Kai klaidų riba pagauna klaidą, ji gali padidinti nesėkmių skaitiklį. Jei nesėkmių skaitiklis viršija ribą, klaidų riba gali parodyti vartotojui pranešimą, nurodantį, kad funkcija laikinai nepasiekiama, ir užkirsti kelią tolesniems bandymams vykdyti operaciją. Po tam tikro laiko klaidų riba gali „uždaryti grandinę“ ir vėl leisti bandyti vykdyti operaciją.
Išvada
React klaidų ribos (Error Boundaries) yra esminis įrankis kuriant patikimas ir vartotojui draugiškas programas. Įdiegę klaidų ribas, galite apsaugoti visą programą nuo sugriuvimo, pateikti vartotojams elegantišką atsarginę vartotojo sąsają ir registruoti klaidas stebėjimo tarnybose derinimo ir analizės tikslais. Laikydamiesi šiame vadove aprašytų geriausių praktikų ir pažangių strategijų, galite kurti React programas, kurios yra atsparios, patikimos ir suteikia teigiamą vartotojo patirtį net ir susidūrus su netikėtomis klaidomis. Nepamirškite sutelkti dėmesį į naudingų klaidų pranešimų, lokalizuotų pasaulinei auditorijai, teikimą.