노마드코더 리액트 네이티브
Todos
- firebase를 사용하여 복습하기
- 컴포넌트 분리해보기
- 하나하나 주석을 달며 공부하기
import { StatusBar } from "expo-status-bar";
import { useEffect, useState } from "react";
import { StyleSheet, View, SafeAreaView, TextInput, Alert } from "react-native";
import { theme } from "./colors";
import { dbService } from "./firebaseConfig";
import {
collection,
addDoc,
query,
orderBy,
getDoc,
doc,
updateDoc,
deleteDoc,
onSnapshot,
} from "firebase/firestore";
import Header from "./components/Header";
import Items from "./components/Items";
export default function App() {
const [category, setCategory] = useState(true);
const [textValue, setTextValue] = useState("");
const [items, setItems] = useState([]);
const [editText, setEditText] = useState();
const categoryChangeHandler = async (isCategory) => {
setCategory(isCategory);
await updateDoc(doc(dbService, "category", "currentCategory"), {
category: isCategory,
});
};
useEffect(() => {
const q = query(collection(dbService, "items"), orderBy("time", "desc"));
onSnapshot(q, (snapshot) => {
const newItems = snapshot.docs.map((doc) => {
const newItem = {
id: doc.id,
...doc.data(),
};
return newItem;
});
setItems(newItems);
});
const getCategory = async () => {
const snapshot = await getDoc(
doc(dbService, "category", "currentCategory")
);
setCategory(snapshot.data().category);
};
getCategory();
}, []);
const addItemHandler = async () => {
if (textValue === "") return alert("빈 값은 입력할 수 없습니다.");
const newItems = {
time: new Date(),
text: textValue,
category,
isDone: false,
isEdit: false,
};
await addDoc(collection(dbService, "items"), newItems);
setTextValue("");
};
const removeItemHandler = async (id) => {
Alert.alert("Remove Item", "Are you sure?", [
{
text: "Cancel",
style: "cancel",
},
{
text: "Delete",
style: "destructive",
onPress: async () => {
await deleteDoc(doc(dbService, "items", id));
},
},
]);
};
const doneCheckHandler = async (id) => {
const item = items.find((item) => item.id === id);
await updateDoc(doc(dbService, "items", id), {
isDone: !item.isDone,
});
};
const editCheckHandler = async (id) => {
const item = items.find((item) => item.id === id);
console.log(item);
await updateDoc(doc(dbService, "items", id), {
isEdit: !item.isEdit,
});
setEditText(item.text);
};
const editItemHandler = async (id) => {
await updateDoc(doc(dbService, "items", id), {
text: editText,
isEdit: false,
});
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.wrap}>
<Header
category={category}
categoryChangeHandler={categoryChangeHandler}
/>
<TextInput
returnKeyType="done"
onSubmitEditing={addItemHandler}
value={textValue}
onChangeText={setTextValue}
placeholder={category ? "Add a To do" : "Where do you want to go?"}
style={styles.input}
/>
<Items
items={items}
category={category}
setEditText={setEditText}
removeItemHandler={removeItemHandler}
doneCheckHandler={doneCheckHandler}
editCheckHandler={editCheckHandler}
editItemHandler={editItemHandler}
/>
<StatusBar style="light" />
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: theme.background,
},
wrap: {
paddingHorizontal: 20,
},
input: {
backgroundColor: "white",
paddingVertical: 15,
paddingHorizontal: 20,
borderRadius: 30,
marginVertical: 20,
fontSize: 15,
},
});
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
import { theme } from "../colors";
export default function Header({ category, categoryChangeHandler }) {
return (
<View style={styles.header}>
<TouchableOpacity onPress={() => categoryChangeHandler(true)}>
<Text
style={{
...styles.btnText,
color: category ? "white" : theme.grey,
}}
>
Work
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => categoryChangeHandler(false)}>
<Text
style={{
...styles.btnText,
color: !category ? "white" : theme.grey,
}}
>
Travel
</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
header: {
flexDirection: "row",
justifyContent: "space-between",
},
btnText: {
fontSize: 25,
fontWeight: "bold",
},
});
import {
ScrollView,
TextInput,
View,
Text,
TouchableOpacity,
StyleSheet,
} from "react-native";
import { Fontisto, AntDesign } from "@expo/vector-icons";
import { theme } from "../colors";
export default function Items({
items,
category,
setEditText,
removeItemHandler,
doneCheckHandler,
editCheckHandler,
editItemHandler,
}) {
return (
<ScrollView>
{items.map((item) => {
if (category === item.category) {
return (
<View style={styles.item} key={item.id}>
{item.isEdit ? (
<TextInput
onSubmitEditing={() => editItemHandler(item.id)}
style={styles.editInput}
defaultValue={item.text}
onChangeText={setEditText}
/>
) : (
<Text
style={{
...styles.itemText,
textDecorationLine: item.isDone ? "line-through" : "none",
}}
>
{item.text}
</Text>
)}
<View style={styles.itemBtnBox}>
<TouchableOpacity
onPress={() => editCheckHandler(item.id)}
style={styles.itemBtn}
>
<AntDesign name="edit" size={20} color={theme.grey} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => doneCheckHandler(item.id)}
style={{ ...styles.itemBtn, marginTop: 2 }}
>
<Fontisto
name="checkbox-active"
size={15}
color={theme.grey}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => removeItemHandler(item.id)}>
<Fontisto name="trash" size={18} color={theme.grey} />
</TouchableOpacity>
</View>
</View>
);
}
})}
</ScrollView>
);
}
const styles = StyleSheet.create({
item: {
backgroundColor: theme.itemBackground,
marginBottom: 10,
paddingVertical: 20,
paddingHorizontal: 20,
borderRadius: 15,
flexDirection: "row",
justifyContent: "space-between",
},
itemText: {
color: "white",
fontSize: 16,
fontWeight: "500",
},
itemBtnBox: {
flexDirection: "row",
},
itemBtn: {
marginRight: 12,
},
editInput: {
width: "70%",
backgroundColor: theme.grey,
color: "white",
},
});