LÀr dig implementera React Error Boundaries med hooks för att elegant hantera resursladdningsfel, vilket förbÀttrar anvÀndarupplevelsen och applikationens stabilitet.
Robust resursladdning i React: BemÀstra felgrÀnser med Hooks
I moderna webbapplikationer Àr asynkron laddning av resurser en vanlig praxis. Oavsett om det handlar om att hÀmta data frÄn ett API, ladda bilder eller importera moduler, Àr det avgörande för en smidig anvÀndarupplevelse att hantera potentiella fel under resursladdningen. Reacts felgrÀnser (Error Boundaries) erbjuder en mekanism för att fÄnga JavaScript-fel var som helst i deras underordnade komponenttrÀd, logga dessa fel och visa ett fallback-grÀnssnitt istÀllet för att hela applikationen kraschar. Den hÀr artikeln utforskar hur man effektivt anvÀnder felgrÀnser i kombination med React Hooks för att hantera resursladdningsfel.
FörstÄelse för felgrÀnser (Error Boundaries)
Före React 16 kunde ohanterade JavaScript-fel under komponentrendering korrumpera Reacts interna tillstÄnd och orsaka kryptiska fel vid efterföljande renderingar. FelgrÀnser löser detta genom att fungera som "catch-all"-block för fel som intrÀffar i deras barnkomponenter. De Àr React-komponenter som implementerar antingen den ena eller bÄda av följande livscykelmetoder:
static getDerivedStateFromError(error): Denna statiska metod anropas efter att ett fel har kastats av en underordnad komponent. Den tar emot felet som kastades som ett argument och returnerar ett vÀrde för att uppdatera komponentens tillstÄnd.componentDidCatch(error, info): Denna livscykelmetod anropas efter att ett fel har kastats av en underordnad komponent. Den tar emot felet som kastades som ett argument, samt ett objekt med information om vilken komponent som kastade felet. Du kan anvÀnda den för att logga felinformation.
Viktigt att notera Àr att felgrÀnser endast fÄngar fel i renderingsfasen, i livscykelmetoder och i konstruktorer för hela trÀdet under dem. De fÄngar inte fel för:
- HÀndelsehanterare (lÀs mer i avsnittet nedan)
- Asynkron kod (t.ex.
setTimeout- ellerrequestAnimationFrame-callbacks) - Rendering pÄ serversidan (Server-side rendering)
- Fel som kastas i sjÀlva felgrÀnsen (snarare Àn i dess barn)
FelgrÀnser och React Hooks: En kraftfull kombination
Medan klasskomponenter traditionellt anvÀndes för att implementera felgrÀnser, erbjuder React Hooks ett mer koncist och funktionellt tillvÀgagÄngssÀtt. Vi kan skapa en ÄteranvÀndbar useErrorBoundary-hook som kapslar in felhanteringslogiken och erbjuder ett bekvÀmt sÀtt att slÄ in komponenter som kan kasta fel under resursladdning.
Skapa en anpassad useErrorBoundary-hook
HÀr Àr ett exempel pÄ en useErrorBoundary-hook:
import { useState, useCallback } from 'react';
function useErrorBoundary() {
const [error, setError] = useState(null);
const resetError = useCallback(() => {
setError(null);
}, []);
const captureError = useCallback((e) => {
setError(e);
}, []);
const ErrorBoundary = useCallback(({ children, fallback }) => {
if (error) {
return fallback ? fallback : An error occurred: {error.message || String(error)};
}
return children;
}, [error]);
return { ErrorBoundary, captureError, error, resetError };
}
export default useErrorBoundary;
Förklaring:
useState: Vi anvÀnderuseStateför att hantera feltillstÄndet. Det sÀtter initialt felet tillnull.useCallback: Vi anvÀnderuseCallbackför att memorera funktionernaresetErrorochcaptureError. Detta Àr god praxis för att förhindra onödiga omrenderingar om dessa funktioner skickas ner som props.ErrorBoundary-komponent: Detta Àr en funktionell komponent skapad meduseCallbacksom tar emotchildrenoch en valfrifallback-prop. Om ett fel finns i tillstÄndet renderar den antingen den angivnafallback-komponenten eller ett standardfelmeddelande. Annars renderar den sina barn (children). Denna fungerar som vÄr felgrÀns. Beroendearrayen `[error]` sÀkerstÀller att den omrenderas nÀr `error`-tillstÄndet Àndras.captureError-funktion: Denna funktion anvÀnds för att sÀtta feltillstÄndet. Du kommer att anropa denna inuti etttry...catch-block nÀr du laddar resurser.resetError-funktion: Denna funktion rensar feltillstÄndet, vilket gör att komponenten kan omrendera sina barn (och potentiellt försöka ladda resursen igen).
Implementera resursladdning med felhantering
Nu ska vi se hur man anvÀnder denna hook för att hantera resursladdningsfel. TÀnk dig en komponent som hÀmtar anvÀndardata frÄn ett API:
import React, { useState, useEffect } from 'react';
import useErrorBoundary from './useErrorBoundary';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const { ErrorBoundary, captureError, error, resetError } = useErrorBoundary();
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (e) {
captureError(e);
}
};
fetchData();
}, [userId, captureError]);
if (error) {
return (
Failed to load user data. {user.name}
Email: {user.email}
{/* Other user details */}Förklaring:
- Vi importerar
useErrorBoundary-hooken. - Vi anropar hooken för att fÄ
ErrorBoundary-komponenten,captureError-funktionen,error-tillstÄndet ochresetError-funktionen. - Inuti
useEffect-hooken slÄr vi in API-anropet i etttry...catch-block. - Om ett fel intrÀffar under API-anropet anropar vi
captureError(e)för att sÀtta feltillstÄndet. - Om
error-tillstÄndet Àr satt renderar viErrorBoundary-komponenten. Vi tillhandahÄller en anpassadfallback-prop som visar ett felmeddelande och en "Försök igen"-knapp. Ett klick pÄ knappen anroparresetErrorför att rensa feltillstÄndet, vilket utlöser en omrendering och ett nytt försök att hÀmta data. - Om inget fel har intrÀffat och anvÀndardatan har laddats, renderar vi anvÀndarprofilens detaljer.
Hantera olika typer av resursladdningsfel
Olika typer av resursladdningsfel kan krÀva olika hanteringsstrategier. HÀr Àr nÄgra vanliga scenarier och hur man kan hantera dem:
NĂ€tverksfel
NÀtverksfel uppstÄr nÀr klienten inte kan ansluta till servern (t.ex. pÄ grund av ett nÀtverksavbrott eller att servern Àr nere). Exemplet ovan hanterar redan grundlÀggande nÀtverksfel med `response.ok`. Du kanske vill lÀgga till mer sofistikerad feldetektering, till exempel:
//Inuti fetchData-funktionen
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
// ĂvervĂ€g att lĂ€gga till specifik hantering av felkoder
if (response.status === 404) {
throw new Error("User not found");
} else if (response.status >= 500) {
throw new Error("Server error. Please try again later.");
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
}
const data = await response.json();
setUser(data);
} catch (error) {
if (error.message === 'Failed to fetch') {
// Troligtvis ett nÀtverksfel
captureError(new Error('Network error. Please check your internet connection.'));
} else {
captureError(error);
}
}
I det hÀr fallet kan du visa ett meddelande till anvÀndaren som indikerar att det finns ett problem med nÀtverksanslutningen och föreslÄ att de kontrollerar sin internetuppkoppling.
API-fel
API-fel intrÀffar nÀr servern returnerar ett felsvar (t.ex. en 400 Bad Request eller en 500 Internal Server Error). Som visas ovan kan du kontrollera `response.status` och hantera dessa fel pÄ lÀmpligt sÀtt.
Fel vid datatolkning
Fel vid datatolkning (data parsing) intrÀffar nÀr svaret frÄn servern inte Àr i det förvÀntade formatet och inte kan tolkas (t.ex. ogiltig JSON). Du kan hantera dessa fel genom att slÄ in anropet till response.json() i ett try...catch-block:
//Inuti fetchData-funktionen
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (error) {
if (error instanceof SyntaxError) {
captureError(new Error('Failed to parse data from server.'));
} else {
captureError(error);
}
}
Fel vid bildladdning
För bildladdning kan du anvÀnda hÀndelsehanteraren onError pÄ <img>-taggen:
function MyImage({ src, alt }) {
const { ErrorBoundary, captureError } = useErrorBoundary();
const [imageLoaded, setImageLoaded] = useState(false);
const handleImageLoad = () => {
setImageLoaded(true);
};
const handleImageError = (e) => {
captureError(new Error(`Failed to load image: ${src}`));
};
return (
Failed to load image.