เรียนรู้วิธีจัดหมวดหมู่และจัดการข้อผิดพลาดภายใน React Error Boundaries อย่างมีประสิทธิภาพ ปรับปรุงเสถียรภาพของแอปพลิเคชันและประสบการณ์ผู้ใช้
การจัดหมวดหมู่ข้อผิดพลาดของ React Error Boundary: คู่มือฉบับสมบูรณ์
การจัดการข้อผิดพลาดเป็นสิ่งสำคัญในการสร้างแอปพลิเคชัน React ที่แข็งแกร่งและดูแลรักษาได้ง่าย ในขณะที่ React Error Boundaries มีกลไกสำหรับการจัดการข้อผิดพลาดที่เกิดขึ้นระหว่างการแสดงผลอย่างเหมาะสม การทำความเข้าใจ วิธีการ จัดหมวดหมู่และตอบสนองต่อข้อผิดพลาดประเภทต่างๆ เป็นสิ่งสำคัญสำหรับการสร้างแอปพลิเคชันที่ยืดหยุ่นอย่างแท้จริง คู่มือนี้สำรวจแนวทางต่างๆ ในการจัดหมวดหมู่ข้อผิดพลาดภายใน Error Boundaries โดยมีตัวอย่างที่เป็นประโยชน์และข้อมูลเชิงลึกที่นำไปปฏิบัติได้ เพื่อปรับปรุงกลยุทธ์การจัดการข้อผิดพลาดของคุณ
React Error Boundaries คืออะไร
Error Boundaries เปิดตัวใน React 16 เป็นคอมโพเนนต์ React ที่ตรวจจับข้อผิดพลาด JavaScript ได้ทุกที่ในโครงสร้างคอมโพเนนต์ลูกของตน บันทึกข้อผิดพลาดเหล่านั้น และแสดง UI สำรองแทนที่จะทำให้โครงสร้างคอมโพเนนต์ทั้งหมดล่ม พวกมันทำงานคล้ายกับบล็อก try...catch แต่สำหรับคอมโพเนนต์
ลักษณะสำคัญของ Error Boundaries:
- การจัดการข้อผิดพลาดในระดับคอมโพเนนต์: แยกข้อผิดพลาดภายในโครงสร้างคอมโพเนนต์ย่อยเฉพาะ
- การลดระดับอย่างเหมาะสม: ป้องกันไม่ให้แอปพลิเคชันทั้งหมดล่มเนื่องจากข้อผิดพลาดของคอมโพเนนต์เดียว
- UI สำรองที่ควบคุม: แสดงข้อความที่เป็นมิตรกับผู้ใช้หรือเนื้อหาทางเลือกเมื่อเกิดข้อผิดพลาด
- การบันทึกข้อผิดพลาด: อำนวยความสะดวกในการติดตามและแก้ไขข้อผิดพลาดโดยการบันทึกข้อมูลข้อผิดพลาด
เหตุใดจึงต้องจัดหมวดหมู่ข้อผิดพลาดใน Error Boundaries
การตรวจจับข้อผิดพลาดเพียงอย่างเดียวไม่เพียงพอ การจัดการข้อผิดพลาดที่มีประสิทธิภาพต้องทำความเข้าใจว่าเกิดอะไรขึ้นและตอบสนองตามนั้น การจัดหมวดหมู่ข้อผิดพลาดภายใน Error Boundaries มีข้อดีหลายประการ:
- การจัดการข้อผิดพลาดเป้าหมาย: ข้อผิดพลาดประเภทต่างๆ อาจต้องมีการตอบสนองที่แตกต่างกัน ตัวอย่างเช่น ข้อผิดพลาดของเครือข่ายอาจรับประกันกลไกการลองใหม่ ในขณะที่ข้อผิดพลาดการตรวจสอบข้อมูลอาจต้องมีการแก้ไขการป้อนข้อมูลของผู้ใช้
- ปรับปรุงประสบการณ์ผู้ใช้: แสดงข้อความแสดงข้อผิดพลาดที่ให้ข้อมูลมากขึ้นตามประเภทข้อผิดพลาด ข้อความทั่วไป "เกิดข้อผิดพลาดบางอย่าง" จะเป็นประโยชน์น้อยกว่าข้อความเฉพาะที่ระบุปัญหาเครือข่ายหรือข้อมูลนำเข้าที่ไม่ถูกต้อง
- การแก้ไขจุดบกพร่องที่ปรับปรุงแล้ว: การจัดหมวดหมู่ข้อผิดพลาดให้บริบทที่มีค่าสำหรับการแก้ไขจุดบกพร่องและระบุสาเหตุที่แท้จริงของปัญหา
- การตรวจสอบเชิงรุก: ติดตามความถี่ของข้อผิดพลาดประเภทต่างๆ เพื่อระบุปัญหาที่เกิดขึ้นซ้ำๆ และจัดลำดับความสำคัญของการแก้ไข
- UI สำรองเชิงกลยุทธ์: แสดง 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) {
// Update state so the next render will show the fallback UI.
return { hasError: true, error: error };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.error("Caught error:", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
let errorMessage = "Something went wrong.";
if (this.state.error instanceof NetworkError) {
errorMessage = "A network error occurred. Please check your connection and try again.";
} else if (this.state.error instanceof ValidationError) {
errorMessage = "There was a validation error. Please review your input.";
}
return (
<div>
<h2>Error!</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("Network request failed");
error.code = response.status; // Add a custom error code
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) {
// Update state so the next render will show the fallback UI.
return { hasError: true, error: error };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.error("Caught error:", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
let errorMessage = "Something went wrong.";
if (this.state.error.code === 404) {
errorMessage = "Resource not found.";
} else if (this.state.error.code >= 500) {
errorMessage = "Server error. Please try again later.";
}
return (
<div>
<h2>Error!</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: "A network error occurred. Please check your connection.",
retry: true,
},
"INVALID_INPUT": {
message: "Invalid input. Please review your data.",
retry: false,
},
404: {
message: "Resource not found.",
retry: false,
},
500: {
message: "Server error. Please try again later.",
retry: true,
},
"DEFAULT": {
message: "Something went wrong.",
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) {
// Update state so the next render will show the fallback UI.
const errorDetails = handleCustomError(error.message);
return { hasError: true, errorDetails: errorDetails };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.error("Caught error:", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
const { message } = this.state.errorDetails;
return (
<div>
<h2>Error!</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
- ทดสอบการจัดการข้อผิดพลาด: เขียนการทดสอบหน่วยเพื่อตรวจสอบว่า Error Boundaries ของคุณจัดการประเภทข้อผิดพลาดต่างๆ ได้อย่างถูกต้อง
- พิจารณาประสบการณ์ผู้ใช้: แสดงข้อความแสดงข้อผิดพลาดที่เป็นข้อมูลและเป็นมิตรกับผู้ใช้ ซึ่งนำทางผู้ใช้ไปสู่การแก้ไข หลีกเลี่ยงคำศัพท์ทางเทคนิค
- ตรวจสอบอัตราข้อผิดพลาด: ติดตามความถี่ของข้อผิดพลาดประเภทต่างๆ เพื่อระบุปัญหาที่เกิดขึ้นซ้ำๆ และจัดลำดับความสำคัญของการแก้ไข
- Internationalization (i18n): เมื่อนำเสนอข้อความแสดงข้อผิดพลาดแก่ผู้ใช้ ตรวจสอบให้แน่ใจว่าข้อความของคุณมีการแปลเป็นสากลอย่างถูกต้องเพื่อรองรับภาษาและวัฒนธรรมต่างๆ ใช้ไลบรารีเช่น
i18nextหรือ React's Context API เพื่อจัดการการแปล - Accessibility (a11y): ตรวจสอบให้แน่ใจว่าข้อความแสดงข้อผิดพลาดของคุณสามารถเข้าถึงได้สำหรับผู้ใช้ที่มีความพิการ ใช้แอตทริบิวต์ ARIA เพื่อให้บริบทเพิ่มเติมแก่โปรแกรมอ่านหน้าจอ
- ความปลอดภัย: ระมัดระวังเกี่ยวกับข้อมูลที่คุณแสดงในข้อความแสดงข้อผิดพลาด โดยเฉพาะอย่างยิ่งในสภาพแวดล้อมการผลิต หลีกเลี่ยงการเปิดเผยข้อมูลที่ละเอียดอ่อนซึ่งอาจถูกโจมตีได้ ตัวอย่างเช่น อย่าแสดงการติดตามสแต็กดิบให้กับผู้ใช้ปลายทาง
สถานการณ์ตัวอย่าง: การจัดการข้อผิดพลาด API ในแอปพลิเคชันอีคอมเมิร์ซ
พิจารณาแอปพลิเคชันอีคอมเมิร์ซที่ดึงข้อมูลผลิตภัณฑ์จาก API สถานการณ์ข้อผิดพลาดที่อาจเกิดขึ้น ได้แก่:
- ข้อผิดพลาดของเครือข่าย: เซิร์ฟเวอร์ API ไม่พร้อมใช้งานหรือการเชื่อมต่ออินเทอร์เน็ตของผู้ใช้ถูกขัดจังหวะ
- ข้อผิดพลาดในการตรวจสอบสิทธิ์: โทเค็นการตรวจสอบสิทธิ์ของผู้ใช้ไม่ถูกต้องหรือหมดอายุ
- ข้อผิดพลาดไม่พบทรัพยากร: สินค้าที่ร้องขอไม่มีอยู่
- ข้อผิดพลาดของเซิร์ฟเวอร์: เซิร์ฟเวอร์ API พบข้อผิดพลาดภายใน
เมื่อใช้ Error Boundaries และการจัดหมวดหมู่ข้อผิดพลาด แอปพลิเคชันสามารถจัดการกับสถานการณ์เหล่านี้ได้อย่างเหมาะสม:
// Example (Simplified)
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); // Use errorMap as shown previously
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>Error!</h2>
<p>{message}</p>
{retry && <button onClick={() => window.location.reload()}>Retry</button>}
</div>
);
}
return this.props.children;
}
}
คำอธิบาย:
- ฟังก์ชัน
fetchProductตรวจสอบรหัสสถานะการตอบสนอง API และส่งประเภทข้อผิดพลาดเฉพาะตามสถานะ - คอมโพเนนต์
ProductErrorBoundaryจับข้อผิดพลาดเหล่านี้และแสดงข้อความแสดงข้อผิดพลาดที่เหมาะสม - สำหรับข้อผิดพลาดของเครือข่ายและข้อผิดพลาดของเซิร์ฟเวอร์ ปุ่ม "ลองใหม่" จะปรากฏขึ้น ทำให้ผู้ใช้สามารถลองขออีกครั้งได้
- สำหรับข้อผิดพลาดในการตรวจสอบสิทธิ์ ผู้ใช้อาจถูกเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบ
- สำหรับข้อผิดพลาดไม่พบทรัพยากร ข้อความที่ระบุว่าสินค้าไม่มีอยู่จะปรากฏขึ้น
สรุป
การจัดหมวดหมู่ข้อผิดพลาดภายใน React Error Boundaries เป็นสิ่งสำคัญสำหรับการสร้างแอปพลิเคชันที่เป็นมิตรกับผู้ใช้ โดยใช้เทคนิคต่างๆ เช่น การตรวจสอบ instanceof รหัสข้อผิดพลาด และการแมปข้อผิดพลาดแบบรวมศูนย์ คุณสามารถจัดการสถานการณ์ข้อผิดพลาดต่างๆ ได้อย่างมีประสิทธิภาพและมอบประสบการณ์การใช้งานที่ดีขึ้น อย่าลืมปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการข้อผิดพลาด การบันทึก และการทดสอบ เพื่อให้แน่ใจว่าแอปพลิเคชันของคุณจัดการกับสถานการณ์ที่ไม่คาดคิดได้อย่างเหมาะสม
ด้วยการใช้กลยุทธ์เหล่านี้ คุณสามารถปรับปรุงเสถียรภาพและการดูแลรักษาแอปพลิเคชัน React ของคุณได้อย่างมาก มอบประสบการณ์ที่ราบรื่นและเชื่อถือได้มากขึ้นสำหรับผู้ใช้ของคุณ โดยไม่คำนึงถึงตำแหน่งที่ตั้งหรือภูมิหลังของพวกเขา
แหล่งข้อมูลเพิ่มเติม: