5. การจัดการ Loading และ Error State
ในบทเรียนก่อนหน้า เราได้เรียนรู้วิธีการใช้ Axios library เพื่อเชื่อมต่อกับ API ใน React Native กันไปแล้ว แต่การเชื่อมต่อ API ไม่ได้ราบรื่นเสมอไป อาจมีช่วงเวลาที่ API ตอบสนองช้า หรือเกิดข้อผิดพลาดขึ้นได้ ดังนั้น การจัดการสถานะ Loading (กำลังโหลด) และ Error (ข้อผิดพลาด) จึงเป็นสิ่งสำคัญอย่างยิ่ง เพื่อให้ผู้ใช้ได้รับประสบการณ์ที่ดีและเข้าใจสถานะของแอปพลิเคชัน
ทำไมต้องจัดการ Loading และ Error State?
ลองนึกภาพว่าคุณกำลังใช้แอปพลิเคชันที่ต้องดึงข้อมูลจาก API แต่หน้าจอแสดงผลว่างเปล่า ไม่มีอะไรเกิดขึ้นเลย ผู้ใช้จะไม่รู้ว่าแอปพลิเคชันกำลังทำงานอยู่ หรือเกิดปัญหาอะไรขึ้นกันแน่ การแสดง Loading indicator (ตัวบ่งชี้ว่ากำลังโหลด) จะช่วยให้ผู้ใช้ทราบว่าแอปพลิเคชันกำลังทำงาน และรอข้อมูลจาก API อยู่
ในทำนองเดียวกัน หากเกิดข้อผิดพลาดในการดึงข้อมูลจาก API การปล่อยให้แอปพลิเคชันหยุดทำงาน หรือแสดงผลข้อมูลที่ไม่ถูกต้อง จะทำให้ผู้ใช้สับสนและผิดหวัง การแสดง Error message (ข้อความแสดงข้อผิดพลาด) ที่ชัดเจน จะช่วยให้ผู้ใช้เข้าใจปัญหาที่เกิดขึ้น และอาจมีแนวทางแก้ไขเบื้องต้น เช่น การลองใหม่อีกครั้ง หรือติดต่อผู้พัฒนา
ดังนั้น การจัดการ Loading และ Error state จึงเป็นสิ่งจำเป็นเพื่อให้:
- ประสบการณ์ผู้ใช้ (User Experience - UX) ที่ดี: ผู้ใช้ทราบสถานะของแอปพลิเคชัน และรู้สึกว่าแอปพลิเคชันตอบสนองอย่างเหมาะสม
- การดีบัก (Debugging) ที่ง่ายขึ้น: ข้อความแสดงข้อผิดพลาดช่วยให้ผู้พัฒนาเข้าใจปัญหาที่เกิดขึ้น และแก้ไขได้รวดเร็วขึ้น
- ความน่าเชื่อถือของแอปพลิเคชัน: การจัดการข้อผิดพลาดอย่างเหมาะสม ช่วยป้องกันไม่ให้แอปพลิเคชันหยุดทำงาน หรือแสดงผลข้อมูลที่ไม่ถูกต้อง
การแสดง Loading Indicator
การแสดง Loading indicator เป็นวิธีที่ง่ายที่สุดในการบอกผู้ใช้ว่าแอปพลิเคชันกำลังทำงานอยู่ โดยทั่วไป Loading indicator จะเป็น animated icon (ไอคอนเคลื่อนไหว) หรือข้อความที่แสดงความคืบหน้า
ใน React Native เราสามารถใช้ Component ActivityIndicator เพื่อแสดง Loading indicator ได้ ตัวอย่าง:
import React, { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native';
import axios from 'axios';
const MyComponent = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true); // Initial state is loading
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
setData(response.data);
} catch (error) {
setError(error);
} finally {
setLoading(false); // Set loading to false regardless of success or failure
}
};
fetchData();
}, []);
if (loading) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" color="#0000ff" />
<Text>กำลังโหลดข้อมูล...</Text>
</View>
);
}
if (error) {
return (
<View style={styles.container}>
<Text>เกิดข้อผิดพลาด: {error.message}</Text>
</View>
);
}
return (
<View style={styles.container}>
<Text>ข้อมูลที่ได้รับ: {data.title}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default MyComponent;
ในตัวอย่างนี้ เราใช้ useState hook เพื่อสร้าง state variable ชื่อ loading ซึ่งมีค่าเริ่มต้นเป็น true เมื่อ Component ถูก render ครั้งแรก useEffect hook จะเรียกฟังก์ชัน fetchData เพื่อดึงข้อมูลจาก API
ภายในฟังก์ชัน fetchData เราตั้งค่า loading เป็น true ก่อนที่จะเรียก API เมื่อ API response กลับมา ไม่ว่าจะเป็นสำเร็จหรือล้มเหลว เราจะตั้งค่า loading เป็น false เสมอ
จากนั้นในส่วนของ JSX เราจะตรวจสอบค่าของ loading ถ้าเป็น true เราจะแสดง ActivityIndicator และข้อความ "กำลังโหลดข้อมูล..." ถ้าเป็น false เราจะแสดงข้อมูลที่ได้รับจาก API
การแสดง Error Message
เมื่อเกิดข้อผิดพลาดในการดึงข้อมูลจาก API เราควรแสดง Error message ที่ชัดเจน เพื่อให้ผู้ใช้เข้าใจปัญหาที่เกิดขึ้น
ในตัวอย่างก่อนหน้า เราได้สร้าง state variable ชื่อ error เพื่อเก็บ error object ที่ได้รับจาก API ถ้า error มีค่า เราจะแสดงข้อความ "เกิดข้อผิดพลาด: " ตามด้วย error message
เราสามารถปรับแต่ง Error message ให้มีความหมายมากขึ้น โดยการตรวจสอบประเภทของ error และแสดงข้อความที่เหมาะสม ตัวอย่าง:
// ... (ส่วนที่เหลือของโค้ดเหมือนเดิม)
if (error) {
let errorMessage = 'เกิดข้อผิดพลาดที่ไม่ทราบสาเหตุ';
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
errorMessage = `HTTP Error: ${error.response.status} - ${error.response.data.message || 'No message'}`;
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
errorMessage = 'ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์';
} else {
// Something happened in setting up the request that triggered an Error
errorMessage = `Error: ${error.message}`;
}
return (
<View style={styles.container}>
<Text>เกิดข้อผิดพลาด: {errorMessage}</Text>
</View>
);
}
// ... (ส่วนที่เหลือของโค้ดเหมือนเดิม)
ในตัวอย่างนี้ เราตรวจสอบว่า error มี property response หรือ request หรือไม่ ถ้ามี เราจะแสดง Error message ที่มีความหมายมากขึ้น เช่น HTTP status code หรือข้อความ "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์"
การ Retry Request
ในบางกรณี ข้อผิดพลาดในการดึงข้อมูลจาก API อาจเกิดขึ้นชั่วคราว เช่น ปัญหาเครือข่าย หรือเซิร์ฟเวอร์ไม่ว่าง การ Retry request (ลองส่งคำขอใหม่อีกครั้ง) อาจช่วยแก้ไขปัญหาได้
เราสามารถเพิ่มปุ่ม "ลองใหม่" ใน Error message เพื่อให้ผู้ใช้สามารถ Retry request ได้ง่ายขึ้น ตัวอย่าง:
import React, { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator, Button, StyleSheet } from 'react-native';
import axios from 'axios';
const MyComponent = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const fetchData = async () => {
setLoading(true);
setError(null); // Clear previous error
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
setData(response.data);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
if (loading) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" color="#0000ff" />
<Text>กำลังโหลดข้อมูล...</Text>
</View>
);
}
if (error) {
return (
<View style={styles.container}>
<Text>เกิดข้อผิดพลาด: {error.message}</Text>
<Button title="ลองใหม่" onPress={fetchData} />
</View>
);
}
return (
<View style={styles.container}>
<Text>ข้อมูลที่ได้รับ: {data.title}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default MyComponent;
ในตัวอย่างนี้ เราเพิ่ม Button component ที่มี title เป็น "ลองใหม่" เมื่อผู้ใช้กดปุ่มนี้ ฟังก์ชัน fetchData จะถูกเรียกอีกครั้ง ทำให้แอปพลิเคชัน Retry request
Trade-off: การ Retry request อาจทำให้เกิดปัญหา loop (วนซ้ำ) ถ้าข้อผิดพลาดเกิดขึ้นอย่างต่อเนื่อง เราสามารถเพิ่ม counter (ตัวนับ) เพื่อจำกัดจำนวนครั้งที่ Retry request ได้
ข้อควรระวัง: การ Retry request ควรทำอย่างระมัดระวัง เพื่อหลีกเลี่ยงการสร้างภาระให้กับเซิร์ฟเวอร์มากเกินไป
สรุป
การจัดการ Loading และ Error state เป็นสิ่งสำคัญในการพัฒนาแอปพลิเคชัน React Native ที่มีการเชื่อมต่อ API การแสดง Loading indicator ช่วยให้ผู้ใช้ทราบว่าแอปพลิเคชันกำลังทำงานอยู่ การแสดง Error message ที่ชัดเจน ช่วยให้ผู้ใช้เข้าใจปัญหาที่เกิดขึ้น และการ Retry request อาจช่วยแก้ไขปัญหาได้
ในบทเรียนนี้ เราได้เรียนรู้วิธีการแสดง Loading indicator โดยใช้ ActivityIndicator component, การแสดง Error message โดยใช้ state variable และการ Retry request โดยใช้ Button component
หวังว่าบทเรียนนี้จะเป็นประโยชน์ในการพัฒนาแอปพลิเคชัน React Native ของคุณนะครับ