המטרה בחלק הזה היא להבין איך לקבל ולהכניס מידע, לטפל בשגיאות ולהציג נתונים. כדי שיהיה לנו מידע להשתמש בו, נשתמש ב-json server. הוא בחינם ויש בו מידע לדוגמא שאפשר להשתמש בו.
הכתובת שנשתמש בה לשליפת נתונים היא:
https://jsonplaceholder.typicode.com/posts?_limit=10
GET Requests
פונקציית fetchData תשלוף את הנתונים על ידי פקודת fetch ואחר כך נהפוך את מה שקבלנו ל-json.
const fetchData = async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts?_limit=10`
);
const data = await response.json();
}
כדי להשתמש במידע נשמור אותו על ידי useState לתוך משתנה.
const [postList, setPostList] = useState([]);
const fetchData = async (limit = 10) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
);
const data = await response.json();
setPostList(data);
}
אנחנו רוצים לקרוא ל-fetchData כשהקומפוננטה עולה. נקרא ל-useEffect כדי לעשות את זה.
useEffect(() => {
fetchData();
}, [])
כדי להציג את הרשימה נשתמש ב-FlatList.
import { Text, SafeAreaView, View, StyleSheet,
FlatList } from "react-native";
import { useState, useEffect } from "react";
export default function Index() {
const [postList, setPostList] = useState<any[]>([])
const fetchData = async (limit = 10) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
);
const data = await response.json();
setPostList(data);
}
useEffect(() => {
fetchData();
}, [])
return (
<SafeAreaView style={styles.mainContainer}>
<View style={styles.listContainer}>
<FlatList
data={postList}
renderItem = {({ item }) => {
return(
<View style={styles.card}>
<Text style={styles.titleText}>{item.title}</Text>
<Text style={styles.bodyText}>{item.body}</Text>
</View>
)
}}
ListEmptyComponent={<Text>No Posts Found</Text>}
ListHeaderComponent={<Text style={styles.headerText}>Post List</Text>}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
mainContainer: {
flex: 1,
backgroundColor: "#f5f5f5",
paddingTop: 25
},
listContainer:{
flex: 1,
paddingHorizontal: 16
},
card: {
backgroundColor: "#FFFFFF",
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 10
},
titleText: {
fontSize: 22,
},
bodyText: {
fontSize: 16,
color: "#666666",
},
headerText: {
fontSize: 26,
textAlign: 'center',
marginBottom: 12
}
})

Loading State
נראה איך להציג למשתמש שהמידע נטען. נשתמש במצב של isLoading כשהערך ההתחלתי הוא true מכיוון שהמידע נטען מייד בכניסה לדף. בסיום הטעינה נעדכן את הערך שלו ל-false. בהתאם לערך של isLoading נציג ספינר טעינה.
export default function Index() {
...
const [isLoading, setIsLoading] = useState(true);
const fetchData = async (limit = 10) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
);
const data = await response.json();
setPostList(data);
setIsLoading(false);
}
if(isLoading){
return(
<SafeAreaView style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#000" />
<Text>Loading...</Text>
</SafeAreaView>
)
}
...
return(
...
)
}
loadingContainer: {
flex: 1,
backgroundColor: "#f5f5f5",
paddingTop: 25,
justifyContent: 'center',
alignItems: 'center'
}
Pull to Refresh
נשתמש בפונקציונליות של רענון המידע בעמוד על ידי משיכת העמוד מבלי צורך לטעון את כל האפליקציה מחדש.
קודם ניצור את המשתנה שיעקוב אחרי רענון המידע:
const [refreshing, setRefreshing] = useState(false);
נצמיד את ערך המשתנה לערך הרענון של הרשימה שלנו. נוסיף את פעולת onRefresh שמצפה לקבל פונציה שאומרת מה לעשות במקרה של רענון.
בפונקציה נשנה את הערך של משתנה refreshing ל-trueת נטען את הרשימה שוב ונחזיר את הערך ל-false.
export default function Index() {
const [postList, setPostList] = useState<any[]>([])
const [isLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const fetchData = async (limit = 10) => {
...
}
const handleRefresh = () => {
setRefreshing(true);
fetchData(20);
setRefreshing(false);
}
...
return (
<SafeAreaView style={styles.mainContainer}>
<View style={styles.listContainer}>
<FlatList
data={postList}
renderItem = {({ item }) => {
return(
<View style={styles.card}>
<Text style={styles.titleText}>{item.title}</Text>
<Text style={styles.bodyText}>{item.body}</Text>
</View>
)
}}
ListEmptyComponent={<Text>No Posts Found</Text>}
ListHeaderComponent={<Text style={styles.headerText}>Post List</Text>}
refreshing={refreshing}
onRefresh={handleRefresh}
/>
</View>
</SafeAreaView>
);
}
POST Request
נשתמש ב-Json Api של קודם על מנת לבצע פעולת post לכותרת ותוכן של פוסט.
ניצור משתנים שיקלטו את הכותרת והתוכן החדשים. ניצור גם משתנה שיעקוב אחרי פעולת ה-submit.
const [postTitle, setPostTitle] = useState("");
const [postBody, setPostBody] = useState("");
const [isPosting, setIsPosting] = useState(false);
נחבר את המשתנים האלה לשדות בטופס. נשתמש באותו הדף שהשתמשנו בו עד עכשיו. נשתמש ב-react fragment על מנת להחזיר קומפוננטה נוספת.
נוסיף 2 שדות קלט וכפתור שיבצע את פעולת ה-submit.
return (
<SafeAreaView style={styles.mainContainer}>
<>
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
placeholder="Post Title"
value={postTitle}
onChangeText={setPostTitle}
/>
<TextInput
style={styles.input}
placeholder="Post Body"
value={postBody}
onChangeText={setPostBody}
/>
<Button
title={isPosting ? "Adding..." : "Add Post"}
onPress={addPost}
disabled={isPosting}
/>
</View>
...
</>
...
inputContainer: {
backgroundColor: "#FFFFFF",
padding: 16,
borderRadius: 8,
borderWidth: 1,
margin: 16,
},
input: {
height: 40,
borderColor: "gray",
borderWidth: 1,
marginBottom: 8,
padding: 8,
borderRadius: 8,
},
נגדיר את addPost שמטפלת בשליחת הטופס. נפנה ל-API עם פעולת post ונשלח את הנתונים. נקבל בחזרה את ה-post שיצרנו ונוסיף אותו לרשימת הפוסטים הקיימת.
const addPost = async () => {
setIsPosting(true);
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: postTitle,
body: postBody,
}),
}
);
const newPost = await response.json();
setPostList([newPost, ...postList]);
setPostTitle("");
setPostBody("");
setIsPosting(false);
}
Error Handling
נטפל בשגיאות שמגיעות לאחר פעולות מול DB. נגדיר משתנה שיאחסן את השגיאה.
const [error, setError] = useState("");
נוסיף בלוקים של try-catch על מנת לתפוס את השגיאות שמגיעות.
זה הקוד המלא של הדוגמא:
import { Text, SafeAreaView, View, StyleSheet,
FlatList, ActivityIndicator, TextInput, Button } from "react-native";
import { useState, useEffect } from "react";
export default function Index() {
const [postList, setPostList] = useState<any[]>([])
const [isLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [postTitle, setPostTitle] = useState("");
const [postBody, setPostBody] = useState("");
const [isPosting, setIsPosting] = useState(false);
const [error, setError] = useState("");
const fetchData = async (limit = 10) => {
try{
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
);
const data = await response.json();
setPostList(data);
setIsLoading(false);
} catch(error){
console.error("Error fetching data:", error);
setIsLoading(false);
setError("Failed to fetch post list.");
}
}
const handleRefresh = () => {
setRefreshing(true);
fetchData(20);
setRefreshing(false);
}
const addPost = async () => {
setIsPosting(true);
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: postTitle,
body: postBody,
}),
}
);
const newPost = await response.json();
setPostList([newPost, ...postList]);
setPostTitle("");
setPostBody("");
setError("");
} catch (error) {
console.error("Error adding new post:", error);
setError("Failed to add new post.");
}
setIsPosting(false);
}
useEffect(() => {
fetchData();
}, [])
if(isLoading){
return(
<SafeAreaView style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#000" />
<Text>Loading...</Text>
</SafeAreaView>
)
}
return (
<SafeAreaView style={styles.mainContainer}>
{ error ? (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>{error}</Text>
</View>
) : (
<>
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
placeholder="Post Title"
value={postTitle}
onChangeText={setPostTitle}
/>
<TextInput
style={styles.input}
placeholder="Post Body"
value={postBody}
onChangeText={setPostBody}
/>
<Button
title={isPosting ? "Adding..." : "Add Post"}
onPress={addPost}
disabled={isPosting}
/>
</View>
<View style={styles.listContainer}>
<FlatList
data={postList}
renderItem = {({ item }) => {
return(
<View style={styles.card}>
<Text style={styles.titleText}>{item.title}</Text>
<Text style={styles.bodyText}>{item.body}</Text>
</View>
)
}}
ListEmptyComponent={<Text>No Posts Found</Text>}
ListHeaderComponent={<Text style={styles.headerText}>Post List</Text>}
refreshing={refreshing}
onRefresh={handleRefresh}
/>
</View>
</>
)}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
mainContainer: {
flex: 1,
backgroundColor: "#f5f5f5",
paddingTop: 25
},
listContainer:{
flex: 1,
paddingHorizontal: 16
},
card: {
backgroundColor: "#FFFFFF",
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 10
},
titleText: {
fontSize: 22,
},
bodyText: {
fontSize: 16,
color: "#666666",
},
headerText: {
fontSize: 26,
textAlign: 'center',
marginBottom: 12
},
loadingContainer: {
flex: 1,
backgroundColor: "#f5f5f5",
paddingTop: 25,
justifyContent: 'center',
alignItems: 'center'
},
inputContainer: {
backgroundColor: "#FFFFFF",
padding: 16,
borderRadius: 8,
borderWidth: 1,
margin: 16,
},
input: {
height: 40,
borderColor: "gray",
borderWidth: 1,
marginBottom: 8,
padding: 8,
borderRadius: 8,
},
errorContainer: {
backgroundColor: "#FFC0CB",
padding: 16,
borderRadius: 8,
borderWidth: 1,
margin: 16,
alignItems: "center",
},
errorText: {
color: "#D8000C",
fontSize: 16,
textAlign: "center",
},
})