새로운 프로젝트를 시작한다.
code .을 통해서 vscode를 열어준다.
이번 프로젝트는 React Natvie
TouchableOpacity을 사용해볼것
https://reactnative.dev/docs/touchableopacity
App.js를 아래와 같이 수정해준다.
expo앱으로 실행한다.
앱에는 아래와 같이 출력됨.
-> press here을 클릭하면 위의 count가 증가
import { StatusBar } from "expo-status-bar";
import { useState } from "react";
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
import { theme } from "./color";
export default function App() {
return (
<View style={styles.container}>
<StatusBar style="auto" />
<View style={styles.header}>
<TouchableOpacity>
<Text style={styles.btnText}>Work</Text>
</TouchableOpacity>
<TouchableOpacity>
<Text style={styles.btnText}>Travel</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: theme.bg, //테마 적용
paddingHorizontal: 20,
},
header: {
justifyContent: "space-between",
flexDirection: "row",
marginTop: 100,
},
btnText: {
fontSize: 38,
fontWeight: "600",
color: "white",
},
});
color.js를 만들어서 테마를 적용한다.
export const theme = {
bg: "black",
grey: "#3A3D40",
};
터치가 잘 되는지 확인한다.
usestate를 추가한다.
그리고 클릭한 상태에 따라 스타일을 적용한다. 기본값은 true-
Work 버튼의 TouchableOpacity 의 onpress일 경우 work 함수 실행
work함수는 working를 true로 저장한다.
Travle 버튼의 TouchableOpacity 를 onpress할 경우 travle 함수 실행
working을 false로 저장한다.
z
import { StatusBar } from "expo-status-bar";
import { useState } from "react";
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
import { theme } from "./color";
export default function App() {
const [working, setWorking] = useState(true);
const travel = () => setWorking(false);
const work = () => setWorking(true);
return (
<View style={styles.container}>
<StatusBar style="auto" />
<View style={styles.header}>
<TouchableOpacity onPress={work}>
<Text
style={{ ...styles.btnText, color: working ? "white" : theme.grey }}
>
Work
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={travel}>
<Text
style={{
...styles.btnText,
color: !working ? "white" : theme.grey,
}}
>
Travel
</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: theme.bg, //테마 적용
paddingHorizontal: 20,
},
header: {
justifyContent: "space-between",
flexDirection: "row",
marginTop: 100,
},
btnText: {
fontSize: 38,
fontWeight: "600",
color: "white",
},
});
work를 클릭하면 Work 버튼의 font 색상이 흰색으로 변경
Travel 을 클릭 시 Travle 색상이 흰색으로 변경된다.
https://reactnative.dev/docs/textinput
이번에는 버튼아래에 textInput을 추가할거임
TouchableOpacity 아래에 TextInput을 추가한다.
</Text>
</TouchableOpacity>
</View>
<View>
<TextInput
placeholder={working ? "할일 추가" : "어디로 여행 갈까요?"}
style={styles.input}
/>
</View>
</View>
input: {
backgroundColor: "white",
paddingVertical: 15,
paddingHorizontal: 20,
borderRadius: 30,
marginTop: 20,
fontSize: 18,
},
아래는 영어로 되어있는데
Work선택 시에는 할일 추가
Travle을 선택 시에는 어디로 여행 갈까요?
placeholder로 출력됨
아래와 같이 keyboardType을 변경할 수 있다(참고만 하기 )
그리고 input이 변경될 때마다 state에 text를 저장하도록
useState와 함수를 추가한다.
const [text, setText] = useState("");
const onChangeText = (payload) => setText(payload);
<View>
<TextInput
onChangeText={onChangeText}
value={text}
placeholder={working ? "할일 추가" : "어디로 여행 갈까요?"}
style={styles.input}
/>
</View>
그리고 키보드에서 할일을 입력하고 <완료>를 누르면 alert창이 출력되게 테스트한다.
//완료 누르면 실행
const addTodo = () => {
if (text === "") return;
alert(text);
};
<View>
<TextInput
onSubmitEditing={addTodo}
returnKeyLabel="완료"
onChangeText={onChangeText}
value={text}
placeholder={working ? "할일 추가" : "어디로 여행 갈까요?"}
style={styles.input}
/>
</View>
위와같이 alert창이 정상적으로 출력되면
addTodo 함수를 수정한다.
const [toDos, setToDos] = useState([]);
//완료 누르면 실행
const addTodo = () => {
if (text === "") return;
const newTodo = {
id: Date.now(),
text: text,
working: working,
};
setToDos((prev) => [...prev, newTodo]); //기존의 일정에 추가함
setText(""); // text 초기화
// console.log(toDos);
};
그리고 입력한 todos를 map으로 반복하여 모두 출력한다.
스크롤 뷰를 사용한다.
<View>
<TextInput
onSubmitEditing={addTodo}
returnKeyLabel="완료"
onChangeText={onChangeText}
value={text}
placeholder={working ? "할일 추가" : "어디로 여행 갈까요?"}
style={styles.input}
/>
<ScrollView>
{toDos.map((todo) => (
<View style={styles.toDo} key={todo.id}>
<Text style={styles.toDoText}>{todo.text}</Text>
</View>
))}
</ScrollView>
</View>
input: {
backgroundColor: "white",
paddingVertical: 15,
paddingHorizontal: 20,
borderRadius: 30,
marginVertical: 20,
fontSize: 18,
},
toDo: {
backgroundColor: theme.grey,
marginBottom: 10,
paddingVertical: 20,
paddingHorizontal: 20,
borderRadius: 15,
},
toDoText: {
color: "white",
fontSize: 16,
fontWeight: "500",
},
그 이후
Work를 클릭하면 Work에 등록한 할일이 출력되야하고
Travel을 클릭하면 Travle에 추가한 할 일이 출력되야한다.
<ScrollView>
{toDos.map((todo) =>
todo.working === working ? (
<View style={styles.toDo} key={todo.id}>
<Text style={styles.toDoText}>{todo.text}</Text>
</View>
) : null
)}
</ScrollView>
로컬에 저장
https://docs.expo.dev/versions/latest/sdk/async-storage/
import 하기
import AsyncStorage from "@react-native-async-storage/async-storage";
//처음 한 번만 실행
useEffect(() => {
loadToDos();
}, []);
// 로컬 스토리지에서 추가된 할 일 가져오기
const loadToDos = async () => {
try {
const s = await AsyncStorage.getItem("my-todos");
setToDos(s != null ? JSON.parse(s) : []); //null이 아니면 json.parse를 통해 객체로 변환하고 null이면 빈 배열
} catch (e) {
console.log(e);
}
};
// 로컬 스토리지에 todo 저장
const saveToDos = async (toSave) => {
try {
await AsyncStorage.setItem("my-todos", JSON.stringify(toSave));
} catch (e) {
console.log(e);
}
};
addTodo 수정
//완료 누르면 실행
const addTodo = async () => {
//아무것도 입력하지 않았으면 리턴한다.
if (text === "") return;
const newTodo = {
id: Date.now(),
text: text,
working: working,
};
//새 할일들
const newTodos = [...toDos, newTodo];
setToDos(newTodos);
setText("");
//로컬에 저장하기
await saveToDos(newTodos);
};
todos를 saveToDos에 저장하면 업데이트가 안된 상태로 저장이 될 수 있음
그래서 바로 newTodos로 업데이트한다.
r로 프로젝트 재 실행을 하여 할 일을 추가하고 다시
r로 재실행 시 데이터가 유지되는지 확인한다.
삭제 아이콘을 추가한다.
<ScrollView>
{toDos.map((todo) =>
todo.working === working ? (
<View style={styles.toDo} key={todo.id}>
<Text style={styles.toDoText}>{todo.text}</Text>
<TouchableOpacity>
<Text>❌</Text>
</TouchableOpacity>
</View>
) : null
)}
</ScrollView>
스타일도 추가해준다.
toDo: {
backgroundColor: theme.grey,
marginBottom: 10,
paddingVertical: 20,
paddingHorizontal: 20,
borderRadius: 15,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
},
크리고 x아이콘을 클릭하면 로컬스토리지에서 삭제가 실행도록
메서드를 추가한다.
filter는 새로운 배열을 만들어줌
삭제할 할일의 id와 같지 않은 일정들로만 구성
//삭제 메서드
const deleteTodo = (id) => {
const newTodos = toDos.filter((todo) => todo.id !== id);
setToDos(newTodos); //state에 저장
saveToDos(newTodos); //로컬 스토리지에저장
};
<ScrollView>
{toDos.map((todo) =>
todo.working === working ? (
<View style={styles.toDo} key={todo.id}>
<Text style={styles.toDoText}>{todo.text}</Text>
<TouchableOpacity onPress={() => deleteTodo(todo.id)}>
<Text>❌</Text>
</TouchableOpacity>
</View>
) : null
)}
</ScrollView>
배포하기