24/4/23 react-native (todoList)

2024. 4. 23. 21:37카테고리 없음

하하호호호 니꼬쌤이랑의 리액트 네이티브 투두리스트 클론코딩을 해봤는디 재밌당 흐음...
코드 리뷰를 해보겠슴다.

 

<App.js>

import { StatusBar } from "expo-status-bar";   //이 컴포넌트는 따로 사용 안했어요
import {             //요번 프로젝트에서 사용한 컴포넌트들
  Alert,                  //사용자에게 알림을 보여주는 컴포넌트 리액트 네이티브 웹에서는 작동이 안된대요
  ScrollView,       
  StyleSheet,
  Text,
  TextInput,                //그냥 인풋이아니라 텍스트 인풋이라는 컴포넌트를 사용했슴
  TouchableOpacity,       //리액트에 버튼과 동일한 기능의 컴포넌트
  View,
  Platform,                    //호호 플랫폼 이게 또 신기함 추가설명은 밑에
} from "react-native";
import { theme } from "./colors";                  //니꼬쌤이 자주쓰는 css 분리 차원에서 만들었지만 실제로는 안씀
import { useEffect, useState } from "react";            //내가 좋아하는 훅 모음 ㅋ
import AsyncStorage from "@react-native-async-storage/async-storage";     //요게 또 재밌는데 로컬스토리지랑
import { Fontisto } from "@expo/vector-icons";                                             //같은 역할이에요 사용법도 거의같음
                                                            //요거는 expo에서 제공하는 아이콘을 간편하게 끌어쓸수 있더래요
export default function App() {
  const [working, setWorking] = useState(true);        //두가지 모드로 토글하기 위한 state 
  const [text, setText] = useState();                         //인풋의 텍스트 저장용 스테이트 
  const [todos, setTodos] = useState({});          //투두들을 일시적으로 담아둘 스테이트
  const travel = () => setWorking(false);          //모드의 true false를 토글해줄 함수 두개~~
  const work = () => setWorking(true);

  const onChangeText = (e) => {    //텍스트인풋이란 컴포넌트에 event를  가져오는 함수
    setText(e);                                  //리액트에서는 event.target.value에 text정보가 들어있었는데
  };                                             //얘는 받아오는 인자 자체가 text임 호호 재미썽

  const saveTodos = async (toSave) => {                                     //어플이 새로 마운트 되면 todos가 휘발되므로
    await AsyncStorage.setItem("@todos", JSON.stringify(toSave));  //정보를 보관할 어씽크 스토리지
  };                                                                                     //방식은 key와 value를 두 인자로 받는데 비동기처리
  const loadTodos = async () => {                                          //해서 써야된다는게 신박함 호호 
    const s = await AsyncStorage.getItem("@todos");  //이제 요 함수로 최초 마운트시 정보를 불러오고
    if (s) {
      setTodos(JSON.parse(s));           //해당정보가 있다면 참고로 해당정보는 오브젝트임 
    }                                                 //제이슨형식으로 저장했었으니까 다시 파싱해서 오브젝트로 바꿔야함
  };
  useEffect(() => {
    loadTodos();
    console.log("마운트");             //유즈 이펙트를 사용해서 마운트시 로드하도록 함 역쉬 유즈 이펙트 쌈뽕하군
  }, []);
  const addTodo = async () => {   //투두를 생성하기위한 함수 async를 사용한 이유는 어씽크 스토리지를 안에서 
    if (text === "") {                          //사용예정
      return
    }

    const newTodos = Object.assign({}, todos, {      //기존todo들은 놔둔채 새로운 todo를 첨가해야하므로
      [Date.now()]: { text, work: working },          //요번에는 assign 매서드를 사용해 본다.
    });                                                     //새로운 투구는 현재시간을 key로 갖고 text정보와 work 정보를 갖음
    setTodos(newTodos);                        //고렇게 만든 오브젝트를 새롭게 todos로 셋팅하고
    await saveTodos(newTodos);         //어씽크 스토리지에 저장하고
    setText("");                     //인풋태그를 비운다.
  };
  // console.log(todos);
  const deleteTodo = async (id) => {               //삭제로직
    if (Platform.OS === "web") {                         //platform을 여기서 사용하는데 os종류를 특정할수 있음
      const ok = confirm("삭제하시겠습니까?");   //신박해 신박해 웹일때는 alert이 안먹는다고함
      if (ok) {                                              //그래서 웹에서 사용시에는 confirm을 사용하게 함
        const newTodos = { ...todos };        //닷닷닷으로 새 오브젝트 만들어주고
        delete newTodos[id];                    //거기서 매개변수로 받아온 지워야될 아이디 정보 받고
        setTodos(newTodos);                 //지우고 스테이트 변경
        await saveTodos(newTodos);            //스토리지에 저장
      }
    } else {
      Alert.alert("삭제", "하시겠습니까?", [          //여기가 이제 웹이 아닐 경운데 요게 또 신박함
        {                                                                //첫번째 인자 두번째 인자는 텍스트를 받는거 같고
          text: "네",                                                   //세번에 인자로배열을 받는데 요 배열에 있는 
          onPress: async () => {                               //오브젝트를 alert 작동시에 보이는 버튼을 만들어줌
            const newTodos = { ...todos };             //호호 그 오브젝트에 첫번째 는 텍스트를 저렇게 받고
            delete newTodos[id];                       //두번째는 onPress라는 함수를 받아서 버튼 클릭시 이벤트를
            setTodos(newTodos);                      //만들어 줄수가 있음  신박해 신박해 ㅋㅋㅋ
            await saveTodos(newTodos);
          },
        },
        { text: "아니오" },              //아니오를 누르면 당연히 이벤트 없으니까 함수 노 필요
      ]);
    }
  };
  return (                                                           //여기서부터가 HTML의 영역
    <View style={styles.container}>                         //View로 모든 골격이 이루어짐
      <StatusBar style="auto" />                             //상태바는 뭐 그냥 기본셋팅
      <View style={styles.header}>
        <TouchableOpacity onPress={work}>           //버튼 영역 누르면 working스테이트를 true로 함
          <Text
            style={{ ...styles.btns, color: working ? "white" : theme.gray }}
          >                                                           //3항 연산자로 working일땐 흰색 아니면 회색으로 보이게함
            업무
          </Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={travel}>              //위에 태그와 이하동문
          <Text
            style={{ ...styles.btns, color: !working ? "white" : theme.gray }}
          >
            여행
          </Text>
        </TouchableOpacity>
      </View>
      <View>
        <TextInput
          returnKeyType="done"          //이건 솔직히 대충들어서 기억이 안난다. 
          onSubmitEditing={addTodo}           //역시 리엑트 네이티브라 속성이 낯설다 ㅋㅋ 
          onChangeText={onChangeText}        //흐음... onsubmitEditing은 말그대로 제출이고
          value={text}                                             //온체인지텍스트가 리액트의 온체인지와 같음
          placeholder={working ? "할일은?" : "갈곳은?"} //워킹상태에 따라 플레이스 홀더 변경
          style={styles.input}
        ></TextInput>
      </View>
      <ScrollView contentContainerStyle={styles.scroll}>        //스크롤을 위한 태그
        {Object.keys(todos).map((key) =>         //요게 또 재밌음 오브젝트 키스나 벨류스 엔트리스는
          todos[key].work === working ? (                          //많이 안써봐서 재밌었음 이제 리스트 형식으로
            <View key={key} style={styles.todo}>                                //바꾸고 해당 key값에 따른 work가 트루냐
              <Text style={styles.todoText}>{todos[key].text}</Text>            //폴스냐에 따라 디스플레이를 바꿔줌
              <TouchableOpacity onPress={() => deleteTodo(key)}>           //해당 투두의 텍스트를 보여주고
                <Fontisto name="trash" size={24} color="white" />        //버튼 달아주고 온프레스 속성에 삭제
              </TouchableOpacity>                                                //함수 달아주고 지워야될 아이디 알아야 되니까
            </View>                                                            //인자로 넘겨주고
          ) : null                                                   //캬 아름다워
        )}                                                          
      </ScrollView
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "black",
    paddingHorizontal: 20,
  },
  header: {
    justifyContent: "space-between",
    flexDirection: "row",
    marginTop: 100,
  },
  btns: {
    fontSize: 44,
    fontWeight: "600",
  },
  input: {
    backgroundColor: "white",
    paddingVertical: 15,                   //패딩 버티컬이랑 호리즌털은 react에 없는거라 또 싱기
    paddingHorizontal: 20,
    borderRadius: 30,
    marginTop: 20,
    fontSize: 20,
  },
  scroll: {},
  todo: {
    marginTop: 5,
    backgroundColor: "gray",
    paddingVertical: 10,
    paddingHorizontal: 20,
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
  },
  todoText: {
    fontSize: 20,
    fontWeight: "500",
  },
});

 

결론 

react-native는 재밌다 

재밌는 컴포넌트가 무궁무진하던데 캬 이거 참 .....

CRUD 중에 UPDATE를 단다고 치면

todo에 업데이트 버튼 컴포넌트를 달고 onPress속성에 update 함수를 달고 똑같이 key가 해당 todo의 id일테니까

update함수에 매개변수로 넘겨주고 인풋태그는 기존에 있던거 그대로 써도... 되겠네 state새로 만들어서 
editing 모드일때는 인풋이 다른기능하도록 삼항 연산자로 조치하면 될것 같고...

이제 완료 버튼 누르면 바꿀 text를 해당 오브젝트의 매개변수로 받은 아이디의 value를 찾아내서 바꾸고

todos 새로 세팅하고 asyncStorage에 저장해주면 되겠군 흐음....