Išsamus vadovas, kaip suprasti ir įdiegti „JavaScript Error Boundaries“ „React“ programoje patikimam klaidų tvarkymui ir sklandžiam vartotojo sąsajos veikimui.
JavaScript klaidų ribos (Error Boundary): „React“ klaidų tvarkymo įgyvendinimo vadovas
„React“ programavimo srityje netikėtos klaidos gali sukelti vartotojams nemalonias patirtis ir programos nestabilumą. Aiškiai apibrėžta klaidų tvarkymo strategija yra būtina kuriant patikimas ir stabilias programas. „React“ klaidų ribos (Error Boundaries) suteikia galingą mechanizmą, leidžiantį sklandžiai tvarkyti klaidas, kylančias jūsų komponentų medyje, apsaugant visą programą nuo gedimo ir leidžiant rodyti atsarginę vartotojo sąsają.
Kas yra klaidų riba (Error Boundary)?
Klaidų riba (Error Boundary) yra „React“ komponentas, kuris pagauna JavaScript klaidas bet kurioje savo antrinių komponentų medžio vietoje, registruoja šias klaidas ir rodo atsarginę vartotojo sąsają vietoje sugedusio komponentų medžio. Klaidų ribos pagauna klaidas atvaizdavimo metu, gyvavimo ciklo metoduose ir viso po jomis esančio medžio konstruktoriuose.
Įsivaizduokite klaidų ribą kaip try...catch
bloką, skirtą „React“ komponentams. Kaip try...catch
blokas leidžia tvarkyti išimtis sinchroniniame JavaScript kode, taip klaidų riba leidžia tvarkyti klaidas, kylančias atvaizduojant jūsų „React“ komponentus.
Svarbi pastaba: klaidų ribos nepagauna klaidų, kylančių:
- Įvykių apdorojimo funkcijose (daugiau sužinosite kituose skyriuose)
- Asinchroniniame kode (pvz.,
setTimeout
arrequestAnimationFrame
atgalinio iškvietimo funkcijose) - Serverio pusės generavime (Server-side rendering)
- Pačioje klaidų riboje išmestų klaidų (o ne jos antriniuose komponentuose)
Kodėl verta naudoti klaidų ribas?
Klaidų ribų naudojimas suteikia keletą svarbių pranašumų:
- Geresnė vartotojo patirtis: Užuot rodę tuščią baltą ekraną ar sunkiai suprantamą klaidos pranešimą, galite parodyti vartotojui draugišką atsarginę vartotojo sąsają, informuojančią, kad kažkas nutiko negerai, ir galbūt pasiūlyti būdą atsigauti (pvz., perkrauti puslapį ar pereiti į kitą skiltį).
- Programos stabilumas: Klaidų ribos neleidžia klaidoms vienoje programos dalyje sugadinti visos programos. Tai ypač svarbu sudėtingoms programoms su daug tarpusavyje susijusių komponentų.
- Centralizuotas klaidų tvarkymas: Klaidų ribos suteikia centralizuotą vietą klaidoms registruoti ir problemų priežastims nustatyti. Tai supaprastina derinimą ir priežiūrą.
- Sklandus veikimo prastėjimas (Graceful Degradation): Galite strategiškai išdėstyti klaidų ribas aplink skirtingas programos dalis, kad užtikrintumėte, jog net jei kai kurie komponentai sugenda, likusi programos dalis veiktų toliau. Tai leidžia sklandžiai prastėti veikimui klaidų akivaizdoje.
Klaidų ribų įgyvendinimas „React“
Norint sukurti klaidų ribą, reikia apibrėžti klasės komponentą, kuris įgyvendina vieną (arba abu) iš šių gyvavimo ciklo metodų:
static getDerivedStateFromError(error)
: šis gyvavimo ciklo metodas iškviečiamas po to, kai antrinis komponentas išmeta klaidą. Jis gauna išmestą klaidą kaip argumentą ir turėtų grąžinti reikšmę, kuri atnaujintų komponento būseną, nurodant, kad įvyko klaida (pvz., nustatanthasError
vėliavėlę įtrue
).componentDidCatch(error, info)
: šis gyvavimo ciklo metodas iškviečiamas po to, kai antrinis komponentas išmeta klaidą. Jis gauna išmestą klaidą kaip argumentą kartu suinfo
objektu, kuriame yra informacija apie tai, kuris komponentas išmetė klaidą. Šį metodą galite naudoti klaidai registruoti tokiose paslaugose kaip „Sentry“ ar „Bugsnag“.
Štai pagrindinis klaidų ribos komponento pavyzdys:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
// Atnaujinti būseną, kad kitas atvaizdavimas parodytų atsarginę vartotojo sąsają.
return {
hasError: true,
error: error
};
}
componentDidCatch(error, info) {
// „componentStack“ pavyzdys:
// in ComponentThatThrows (created by App)
// in MyErrorBoundary (created by App)
// in div (created by App)
// in App
console.error("Užfiksuota klaida:", error, info);
this.setState({
errorInfo: info.componentStack
});
// Taip pat galite registruoti klaidą klaidų pranešimo tarnyboje
//logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// Galite atvaizduoti bet kokią pasirinktinę atsarginę vartotojo sąsają
return (
<div>
<h2>Kažkas nutiko negerai.</h2>
<p>Klaida: {this.state.error ? this.state.error.message : "Įvyko nežinoma klaida."}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.errorInfo && this.state.errorInfo}
</details>
</div>
);
}
return this.props.children;
}
}
Norėdami naudoti klaidų ribą, tiesiog apgaubkite komponentų medį, kurį norite apsaugoti:
<ErrorBoundary>
<MyComponentThatMightThrow/>
</ErrorBoundary>
Praktiniai klaidų ribų naudojimo pavyzdžiai
Panagrinėkime keletą praktinių scenarijų, kur klaidų ribos gali būti ypač naudingos:
1. API klaidų tvarkymas
Gaunant duomenis iš API, klaidos gali kilti dėl tinklo problemų, serverio gedimų ar neteisingų duomenų. Galite apgaubti komponentą, kuris gauna ir rodo duomenis, klaidų riba, kad šios klaidos būtų tvarkomos sklandžiai.
function UserProfile() {
const [user, setUser] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(true);
React.useEffect(() => {
async function fetchData() {
try {
const response = await fetch('/api/user');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (error) {
// Klaidą pagaus ErrorBoundary
throw error;
} finally {
setIsLoading(false);
}
}
fetchData();
}, []);
if (isLoading) {
return <p>Kraunamas vartotojo profilis...</p>;
}
if (!user) {
return <p>Vartotojo duomenų nėra.</p>;
}
return (
<div>
<h2>{user.name}</h2>
<p>El. paštas: {user.email}</p>
</div>
);
}
function App() {
return (
<ErrorBoundary>
<UserProfile />
</ErrorBoundary>
);
}
Šiame pavyzdyje, jei API iškvietimas nepavyksta ar grąžina klaidą, klaidų riba pagaus klaidą ir parodys atsarginę vartotojo sąsają (apibrėžtą klaidų ribos render
metode). Tai neleidžia visai programai sugesti ir pateikia vartotojui informatyvesnį pranešimą. Galėtumėte praplėsti atsarginę vartotojo sąsają, suteikdami galimybę pakartoti užklausą.
2. Trečiųjų šalių bibliotekų klaidų tvarkymas
Naudojant trečiųjų šalių bibliotekas, gali būti, kad jos išmes netikėtų klaidų. Apgaubus komponentus, kurie naudoja šias bibliotekas, klaidų ribomis, galima sklandžiai tvarkyti šias klaidas.
Įsivaizduokite hipotetinę diagramų biblioteką, kuri kartais išmeta klaidas dėl duomenų neatitikimų ar kitų problemų. Galėtumėte apgaubti diagramos komponentą taip:
function MyChartComponent() {
try {
// Atvaizduoti diagramą naudojant trečiosios šalies biblioteką
return <Chart data={data} />;
} catch (error) {
// Šis catch blokas nebus veiksmingas „React“ komponento gyvavimo ciklo klaidoms
// Jis skirtas daugiausia sinchroninėms klaidoms šioje konkrečioje funkcijoje.
console.error("Error rendering chart:", error);
// Apsvarstykite galimybę išmesti klaidą, kad ją pagautų ErrorBoundary
throw error; // Išmetama klaida iš naujo
}
}
function App() {
return (
<ErrorBoundary>
<MyChartComponent />
</ErrorBoundary>
);
}
Jei Chart
komponentas išmeta klaidą, klaidų riba ją pagaus ir parodys atsarginę vartotojo sąsają. Atkreipkite dėmesį, kad try/catch blokas MyChartComponent viduje pagaus tik sinchroninės funkcijos klaidas, o ne komponento gyvavimo ciklo klaidas. Todėl klaidų riba čia yra kritiškai svarbi.
3. Atvaizdavimo (Rendering) klaidų tvarkymas
Klaidos gali kilti atvaizdavimo proceso metu dėl neteisingų duomenų, neteisingų savybių (props) tipų ar kitų problemų. Klaidų ribos gali pagauti šias klaidas ir apsaugoti programą nuo gedimo.
function DisplayName({ name }) {
if (typeof name !== 'string') {
throw new Error('Vardas turi būti eilutė');
}
return <h2>Sveiki, {name}!</h2>;
}
function App() {
return (
<ErrorBoundary>
<DisplayName name={123} /> <!-- Neteisingas savybės (prop) tipas -->
</ErrorBoundary>
);
}
Šiame pavyzdyje DisplayName
komponentas tikisi, kad name
savybė (prop) bus eilutė. Jei vietoj to perduodamas skaičius, bus išmesta klaida, o klaidų riba ją pagaus ir parodys atsarginę vartotojo sąsają.
Klaidų ribos ir įvykių apdorojimo funkcijos (Event Handlers)
Kaip minėta anksčiau, klaidų ribos nepagauna klaidų, kurios kyla įvykių apdorojimo funkcijose. Taip yra todėl, kad įvykių apdorojimo funkcijos paprastai yra asinchroninės, o klaidų ribos pagauna tik klaidas, kurios atsiranda atvaizdavimo metu, gyvavimo ciklo metoduose ir konstruktoriuose.
Norėdami tvarkyti klaidas įvykių apdorojimo funkcijose, turite naudoti tradicinį try...catch
bloką pačioje funkcijoje.
function MyComponent() {
const handleClick = () => {
try {
// Kodas, kuris gali išmesti klaidą
throw new Error('Įvyko klaida įvykio apdorojimo funkcijoje');
} catch (error) {
console.error('Užfiksuota klaida įvykio apdorojimo funkcijoje:', error);
// Apdoroti klaidą (pvz., parodyti klaidos pranešimą vartotojui)
}
};
return <button onClick={handleClick}>Spausk mane</button>;
}
Visuotinis klaidų tvarkymas
Nors klaidų ribos puikiai tinka klaidoms tvarkyti „React“ komponentų medyje, jos neapima visų galimų klaidų scenarijų. Pavyzdžiui, jos nepagauna klaidų, kurios atsiranda už „React“ komponentų ribų, pavyzdžiui, klaidų visuotiniuose įvykių klausytojuose (event listeners) arba kode, kuris vykdomas prieš „React“ inicializavimą.
Norėdami tvarkyti tokio tipo klaidas, galite naudoti window.onerror
įvykių apdorojimo funkciją.
window.onerror = function(message, source, lineno, colno, error) {
console.error('Visuotinis klaidų tvarkytojas:', message, source, lineno, colno, error);
// Registruoti klaidą tarnyboje, pvz., Sentry ar Bugsnag
// Parodyti visuotinį klaidos pranešimą vartotojui (neprivaloma)
return true; // Sustabdyti numatytąjį klaidų apdorojimo elgesį
};
Svarbu: Grąžinus true
iš window.onerror
įvykių apdorojimo funkcijos, naršyklė nerodys numatytojo klaidos pranešimo. Tačiau atsižvelkite į vartotojo patirtį; jei nuslopinate numatytąjį pranešimą, įsitikinkite, kad pateikiate aiškią ir informatyvią alternatyvą.
Geriausios klaidų ribų naudojimo praktikos
Štai keletas geriausių praktikų, kurių reikėtų nepamiršti naudojant klaidų ribas:
- Strategiškai išdėstykite klaidų ribas: Apgaubkite skirtingas programos dalis klaidų ribomis, kad izoliuotumėte klaidas ir neleistumėte joms plisti. Apsvarstykite galimybę apgaubti ištisus maršrutus (routes) ar pagrindines vartotojo sąsajos dalis.
- Pateikite informatyvią atsarginę vartotojo sąsają: Atsarginė vartotojo sąsaja turėtų informuoti vartotoją, kad įvyko klaida, ir galbūt pasiūlyti būdą atsigauti. Venkite rodyti bendrinių klaidų pranešimų, tokių kaip „Kažkas nutiko negerai“.
- Registruokite klaidas: Naudokite
componentDidCatch
gyvavimo ciklo metodą klaidoms registruoti tarnyboje, pvz., „Sentry“ ar „Bugsnag“. Tai padės jums nustatyti problemų priežastį ir pagerinti programos stabilumą. - Nenaudokite klaidų ribų numatomoms klaidoms: Klaidų ribos skirtos tvarkyti netikėtas klaidas. Numatytoms klaidoms (pvz., validacijos klaidoms, API klaidoms) naudokite konkretesnius klaidų tvarkymo mechanizmus, tokius kaip
try...catch
blokai ar specializuoti klaidų tvarkymo komponentai. - Apsvarstykite kelių lygių klaidų ribas: Galite įdėti klaidų ribas vieną į kitą, kad užtikrintumėte skirtingus klaidų tvarkymo lygius. Pavyzdžiui, galite turėti visuotinę klaidų ribą, kuri pagauna visas neapdorotas klaidas ir rodo bendrinį klaidos pranešimą, ir konkretesnes klaidų ribas, kurios pagauna klaidas tam tikruose komponentuose ir rodo išsamesnius klaidų pranešimus.
- Nepamirškite apie serverio pusės generavimą: Jei naudojate serverio pusės generavimą, turėsite tvarkyti klaidas ir serveryje. Klaidų ribos veikia serveryje, tačiau gali prireikti papildomų klaidų tvarkymo mechanizmų, kad būtų pagautos klaidos, kylančios pradinio atvaizdavimo metu.
Pažangios klaidų ribų technikos
1. Naudojant „Render Prop“
Užuot atvaizdavę statinę atsarginę vartotojo sąsają, galite naudoti „render prop“, kad suteiktumėte daugiau lankstumo tvarkant klaidas. „Render prop“ yra funkcijos savybė (prop), kurią komponentas naudoja kažkam atvaizduoti.
class ErrorBoundary extends React.Component {
// ... (kaip ir anksčiau)
render() {
if (this.state.hasError) {
// Naudoti „render prop“, kad atvaizduotumėte atsarginę vartotojo sąsają
return this.props.fallbackRender(this.state.error, this.state.errorInfo);
}
return this.props.children;
}
}
function App() {
return (
<ErrorBoundary fallbackRender={(error, errorInfo) => (
<div>
<h2>Kažkas nutiko negerai!</h2>
<p>Klaida: {error.message}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{errorInfo.componentStack}
</details>
</div>
)}>
<MyComponentThatMightThrow/>
</ErrorBoundary>
);
}
Tai leidžia jums pritaikyti atsarginę vartotojo sąsają kiekvienai klaidų ribai individualiai. fallbackRender
savybė (prop) gauna klaidą ir informaciją apie ją kaip argumentus, leidžiančius rodyti konkretesnius klaidų pranešimus ar imtis kitų veiksmų, priklausomai nuo klaidos.
2. Klaidų riba kaip aukštesnės eilės komponentas (HOC)
Galite sukurti aukštesnės eilės komponentą (HOC), kuris apgaubia kitą komponentą klaidų riba. Tai gali būti naudinga taikant klaidų ribas keliems komponentams, nereikia kartoti to paties kodo.
function withErrorBoundary(WrappedComponent) {
return class WithErrorBoundary extends React.Component {
render() {
return (
<ErrorBoundary>
<WrappedComponent {...this.props} />
</ErrorBoundary>
);
}
};
}
// Naudojimas:
const MyComponentWithErrorHandling = withErrorBoundary(MyComponentThatMightThrow);
Funkcija withErrorBoundary
priima komponentą kaip argumentą ir grąžina naują komponentą, kuris apgaubia originalų komponentą klaidų riba. Tai leidžia lengvai pridėti klaidų tvarkymą bet kuriam jūsų programos komponentui.
Klaidų ribų testavimas
Svarbu testuoti klaidų ribas, kad įsitikintumėte, jog jos veikia teisingai. Galite naudoti testavimo bibliotekas, tokias kaip „Jest“ ir „React Testing Library“, kad testuotumėte savo klaidų ribas.
Štai pavyzdys, kaip testuoti klaidų ribą naudojant „React Testing Library“:
import { render, screen, fireEvent } from '@testing-library/react';
import ErrorBoundary from './ErrorBoundary';
function ComponentThatThrows() {
throw new Error('Šis komponentas išmeta klaidą');
}
test('atvaizduoja atsarginę vartotojo sąsają, kai išmetama klaida', () => {
render(
<ErrorBoundary>
<ComponentThatThrows />
</ErrorBoundary>
);
expect(screen.getByText('Kažkas nutiko negerai.')).toBeInTheDocument();
});
Šis testas atvaizduoja ComponentThatThrows
komponentą, kuris išmeta klaidą. Tada testas patikrina, ar rodoma atsarginė vartotojo sąsaja, kurią atvaizdavo klaidų riba.
Klaidų ribos ir serverio komponentai („React 18+“)
Pristačius serverio komponentus „React 18“ ir vėlesnėse versijose, klaidų ribos ir toliau atlieka gyvybiškai svarbų vaidmenį tvarkant klaidas. Serverio komponentai vykdomi serveryje ir siunčia klientui tik atvaizduotą išvestį. Nors pagrindiniai principai išlieka tie patys, yra keletas niuansų, į kuriuos reikia atsižvelgti:
- Klaidų registravimas serverio pusėje: Įsitikinkite, kad registruojate klaidas, kurios kyla serverio komponentuose, serveryje. Tam gali prireikti naudoti serverio pusės klaidų registravimo sistemą arba siųsti klaidas į klaidų stebėjimo tarnybą.
- Atsarginė vartotojo sąsaja kliento pusėje: Nors serverio komponentai atvaizduojami serveryje, vis tiek reikia pateikti atsarginę vartotojo sąsają kliento pusėje klaidų atveju. Tai užtikrina, kad vartotojas turės nuoseklią patirtį, net jei serveriui nepavyks atvaizduoti komponento.
- Srautinis SSR (Streaming SSR): Naudojant srautinį serverio pusės generavimą (SSR), klaidos gali kilti srauto perdavimo proceso metu. Klaidų ribos gali padėti sklandžiai tvarkyti šias klaidas, atvaizduojant atsarginę vartotojo sąsają paveiktam srautui.
Klaidų tvarkymas serverio komponentuose yra besivystanti sritis, todėl svarbu sekti naujausias geriausias praktikas ir rekomendacijas.
Dažniausios klaidos, kurių reikia vengti
- Per didelis pasikliovimas klaidų ribomis: Nenaudokite klaidų ribų kaip pakaitalo tinkamam klaidų tvarkymui jūsų komponentuose. Visada stenkitės rašyti patikimą ir stabilų kodą, kuris sklandžiai tvarko klaidas.
- Klaidų ignoravimas: Būtinai registruokite klaidas, kurias pagauna klaidų ribos, kad galėtumėte nustatyti problemų priežastį. Neapsiribokite vien atsarginės vartotojo sąsajos rodymu ir klaidos ignoravimu.
- Klaidų ribų naudojimas patvirtinimo (validation) klaidoms: Klaidų ribos nėra tinkamas įrankis tvarkyti validacijos klaidas. Vietoj to naudokite konkretesnes validacijos technikas.
- Klaidų ribų netestavimas: Testuokite savo klaidų ribas, kad įsitikintumėte, jog jos veikia teisingai.
Išvada
Klaidų ribos yra galingas įrankis kuriant patikimas ir stabilias „React“ programas. Suprasdami, kaip efektyviai įgyvendinti ir naudoti klaidų ribas, galite pagerinti vartotojo patirtį, išvengti programos gedimų ir supaprastinti derinimą. Nepamirškite strategiškai išdėstyti klaidų ribų, pateikti informatyvią atsarginę vartotojo sąsają, registruoti klaidas ir kruopščiai testuoti savo klaidų ribas.
Laikydamiesi šiame vadove pateiktų gairių ir geriausių praktikų, galite užtikrinti, kad jūsų „React“ programos bus atsparios klaidoms ir suteiks teigiamą patirtį jūsų vartotojams.