Fetching Data จาก API ด้วย useEffect และ useState
สวัสดีครับ! ในบทที่ 12 เราได้เรียนรู้เกี่ยวกับการบันทึกข้อมูลแบบ Local ด้วย AsyncStorage ไปแล้ว ในบทนี้ เราจะมาเรียนรู้วิธีการดึงข้อมูลจากภายนอกแอปพลิเคชันของเรา หรือที่เรียกกันว่าการ Fetching Data จาก API (Application Programming Interface) โดยเราจะใช้ Hook สองตัวที่สำคัญมากใน React Native นั่นคือ useEffect และ useState
การดึงข้อมูลจาก API เป็นสิ่งจำเป็นสำหรับแอปพลิเคชันส่วนใหญ่ เพราะเรามักจะต้องแสดงข้อมูลที่เปลี่ยนแปลงอยู่เสมอ เช่น ข่าวสาร, ราคาสินค้า, หรือข้อมูลผู้ใช้งาน ซึ่งข้อมูลเหล่านี้มักจะเก็บอยู่ใน Server ที่เราต้องทำการร้องขอ (Request) เพื่อนำมาแสดงผลในแอปของเรา
ทำความเข้าใจกับ useEffect Hook
useEffect Hook เป็น Hook ที่ใช้จัดการ Side Effects ใน React Native Side Effects คืออะไร? มันคือการทำงานบางอย่างที่เกิดขึ้น "ข้างนอก" Component ของเรา เช่น การดึงข้อมูลจาก API, การตั้ง Timer, หรือการ Subscribe Event Listener
โครงสร้างพื้นฐานของ useEffect Hook เป็นดังนี้:
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// โค้ดที่จะทำงานเมื่อ Component ถูก Mount (แสดงผลครั้งแรก)
// และอาจจะทำงานอีกครั้งเมื่อ Dependencies เปลี่ยนแปลง
return () => {
// โค้ดที่จะทำงานเมื่อ Component ถูก Unmount (ถูกถอดออกจากหน้าจอ)
// หรือเมื่อ Dependencies เปลี่ยนแปลงก่อนที่ Effect จะทำงานอีกครั้ง
};
}, [/* Dependencies */]);
return (
// UI ของ Component
);
}
- Callback Function: ฟังก์ชันแรกที่ส่งเข้าไปใน
useEffectคือฟังก์ชันที่จะทำงานเมื่อ Component ถูก Mount หรือเมื่อ Dependencies เปลี่ยนแปลง - Dependencies Array: อาร์เรย์ที่ระบุว่า Effect นี้จะทำงานใหม่เมื่อค่าของตัวแปรใดในอาร์เรย์นี้เปลี่ยนแปลง ถ้าเป็นอาร์เรย์ว่าง
[]หมายความว่า Effect จะทำงานเพียงครั้งเดียวตอน Mount เท่านั้น ถ้าไม่ระบุอาร์เรย์เลย Effect จะทำงานใหม่ทุกครั้งที่ Component Render - Cleanup Function (Optional): ฟังก์ชันที่ return ออกมาจาก Callback Function จะถูกเรียกเมื่อ Component ถูก Unmount หรือเมื่อ Dependencies เปลี่ยนแปลงก่อนที่ Effect จะทำงานอีกครั้ง ฟังก์ชันนี้มักใช้ในการยกเลิก Side Effects เช่น การยกเลิก Subscription หรือการลบ Timer
กรณีใช้งานจริง: การดึงข้อมูลจาก API เมื่อ Component ถูก Mount
import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
async function fetchData() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const json = await response.json();
setData(json);
}
fetchData();
}, []);
if (data === null) {
return <Text>Loading...</Text>;
}
return (
<View>
<Text>Title: {data.title}</Text>
<Text>Completed: {data.completed ? 'Yes' : 'No'}</Text>
</View>
);
}
export default MyComponent;
คำอธิบาย:
- เราใช้
useStateHook เพื่อสร้าง State ที่ชื่อว่าdataซึ่งจะเก็บข้อมูลที่ได้จาก API - เราใช้
useEffectHook โดยมี Dependencies เป็นอาร์เรย์ว่าง[]ซึ่งหมายความว่า Effect จะทำงานเพียงครั้งเดียวตอน Mount เท่านั้น - ใน
useEffectเราสร้างฟังก์ชันfetchDataที่ใช้fetchAPI เพื่อดึงข้อมูลจาก API - เราใช้
async/awaitเพื่อให้โค้ดอ่านง่ายขึ้น และรอให้การดึงข้อมูลเสร็จสิ้นก่อนที่จะนำข้อมูลมา Update State - เมื่อข้อมูลถูกดึงมาได้แล้ว เราจะ Update State
dataด้วยข้อมูลที่ได้ - ก่อนที่ข้อมูลจะถูกดึงมา เราจะแสดงข้อความ "Loading..." เพื่อบอกให้ผู้ใช้ทราบว่ากำลังมีการดึงข้อมูล
- เมื่อข้อมูลถูกดึงมาได้แล้ว เราจะแสดงผลข้อมูลใน Component
การใช้ fetch API
fetch API เป็น API ที่มีอยู่ใน JavaScript สำหรับใช้ในการส่ง HTTP Request ไปยัง Server และรับ Response กลับมา
โครงสร้างพื้นฐานของ fetch API เป็นดังนี้:
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
// ทำงานกับข้อมูลที่ได้
})
.catch(error => {
// จัดการข้อผิดพลาด
});
คำอธิบาย:
fetch('https://api.example.com/data')ส่ง GET Request ไปยัง URL ที่ระบุ.then(response => response.json())แปลง Response ที่ได้ให้อยู่ในรูปแบบ JSON.then(data => { ... })ทำงานกับข้อมูลที่ได้จาก API.catch(error => { ... })จัดการข้อผิดพลาดที่อาจเกิดขึ้นระหว่างการ Fetch Data
ข้อดีของการใช้ Async/Await: ทำให้โค้ดอ่านง่ายและจัดการได้ง่ายกว่า Promise Chains
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
// ทำงานกับข้อมูลที่ได้
} catch (error) {
// จัดการข้อผิดพลาด
}
}
การจัดการ Loading State และ Error Handling
เมื่อเราดึงข้อมูลจาก API สิ่งสำคัญคือต้องจัดการ Loading State และ Error Handling เพื่อให้ผู้ใช้ได้รับประสบการณ์ที่ดี
Loading State: แสดงสถานะ Loading ในขณะที่กำลังดึงข้อมูล
import React, { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator } from 'react-native';
function MyComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true); // เพิ่ม Loading State
useEffect(() => {
async function fetchData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const json = await response.json();
setData(json);
} catch (error) {
console.error(error);
} finally {
setLoading(false); // ปิด Loading State ไม่ว่าจะสำเร็จหรือไม่ก็ตาม
}
}
fetchData();
}, []);
if (loading) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color="#0000ff" />
<Text>Loading...</Text>
</View>
);
}
return (
<View>
<Text>Title: {data.title}</Text>
<Text>Completed: {data.completed ? 'Yes' : 'No'}</Text>
</View>
);
}
export default MyComponent;
Error Handling: จัดการข้อผิดพลาดที่อาจเกิดขึ้นระหว่างการ Fetch Data
import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
function MyComponent() {
const [data, setData] = useState(null);
const [error, setError] = useState(null); // เพิ่ม Error State
useEffect(() => {
async function fetchData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/invalid-endpoint'); // Endpoint ที่ไม่ถูกต้อง
const json = await response.json();
setData(json);
} catch (error) {
console.error(error);
setError(error); // Update Error State
}
}
fetchData();
}, []);
if (error) {
return <Text>Error: {error.message}</Text>;
}
if (data === null) {
return <Text>Loading...</Text>;
}
return (
<View>
<Text>Title: {data.title}</Text>
<Text>Completed: {data.completed ? 'Yes' : 'No'}</Text>
</View>
);
}
export default MyComponent;
ข้อผิดพลาดที่พบบ่อย:
- CORS Error: เกิดขึ้นเมื่อ Server ไม่อนุญาตให้ Client ที่มาจาก Domain อื่นเข้าถึงข้อมูล (Cross-Origin Resource Sharing) วิธีแก้ไขคือให้ Server กำหนด Header
Access-Control-Allow-Originให้ถูกต้อง หรือใช้ Proxy Server - Network Error: เกิดขึ้นเมื่อไม่มีการเชื่อมต่ออินเทอร์เน็ต หรือ Server ไม่สามารถเข้าถึงได้
- JSON Parsing Error: เกิดขึ้นเมื่อ Response ที่ได้จาก Server ไม่ใช่ JSON ที่ถูกต้อง
Performance Considerations:
- Caching: พิจารณาใช้ Caching เพื่อลดจำนวน Request ที่ส่งไปยัง Server โดยเฉพาะข้อมูลที่ไม่ค่อยมีการเปลี่ยนแปลง
- Debouncing/Throttling: ใช้ Debouncing หรือ Throttling เพื่อควบคุมความถี่ในการส่ง Request เมื่อมีการเปลี่ยนแปลง State บ่อยๆ เช่น ใน TextInput
Security Considerations:
- HTTPS: ใช้ HTTPS เสมอเพื่อเข้ารหัสข้อมูลที่ส่งระหว่าง Client และ Server
- API Keys: เก็บ API Keys ไว้ใน Environment Variables และไม่ Commit ลงใน Source Code
สรุป: ในบทนี้เราได้เรียนรู้วิธีการดึงข้อมูลจาก API ด้วย useEffect และ useState Hook รวมถึงการจัดการ Loading State และ Error Handling หวังว่าความรู้เหล่านี้จะเป็นประโยชน์ในการพัฒนาแอปพลิเคชัน React Native ของคุณนะครับ ในบทถัดไป เราจะมาเรียนรู้เกี่ยวกับการใช้งาน Activity Indicator เพื่อแสดงสถานะ Loading ให้สวยงามยิ่งขึ้น