[TIL] React Native : 노마드 코더 투두앱 챌린지 구현

Jade·2023년 3월 21일
1

Today I learn

목록 보기
74/77
post-thumbnail

Challenge
1.remember the state of Header Menu
2.add finished or done state to todo item
3.edit text of todo item (show small text input)

1. 헤더 메뉴의 상태 기억하기

브라우저상의 localStorage와 비슷하게 리액트 네이티브 상에서는 AsyncStorage가 존재한다.
import AsyncStorage from "@react-native-async-storage/async-storage";로 import 해와서 사용할 수 있음.

1번 챌린지의 핵심은 working이라는 state에 저장되는 값을 AsyncStorage에 저장되도록 하는 것이다.

working은 boolean 값을 가지는 상태로, Work 메뉴를 누른 상태(true)인지, Travel 메뉴를 누른 상태(false)인지 구분하도록 해준다.

working을 통해서 해당하는 todo 리스트를 보여주거나 여행 리스트를 보여주는 삼항 연산자의 조건 역할도 해준다.

//travel, work라는 상단 메뉴를 누를 때마다 아래 두 함수가 실행되는데 
//함수 내부에 saveWhere이라는 함수로 working에 해당하는 상태를 저장한다.
  const travel = () => {
    setWorking(false);
    saveWhere(false);
  };

  const work = () => {
    setWorking(true);
    saveWhere(true);
  };

//async await 구문을 사용할 땐 에러처리를 위해 try catch문을 함께 쓰는 게 좋음 
const saveWhere = async (where) => {
    try {
      await AsyncStorage.setItem("@where", JSON.stringify(where));
    } catch (error) {
      console.log(error);
    }
  };

saveWhere라는 함수는 where라는 전달인자를 받아서 AsyncStorage에 저장해준다.
스토리지 사용법은 로컬스토리지와 크게 다를 게 없어서 어렵지 않았다.

스토리지에 저장한 값은 loadWhere라는 함수를 통해서 불러오도록 했는데, 이 loadWhere는 ToDo 리스트를 스토리지에 저장했다가 불러오는 함수인 loadToDos와 로직이 다르지 않고 useEffect를 통해서 처음 마운트 될 때 한 번만 실행되도록 설정했다.

 const loadWhere = async () => {
    const str = await AsyncStorage.getItem("@where");
    setWorking(JSON.parse(str));
  };

useEffect(() => {
    loadToDos();
    loadWhere();
  }, []);




2. 할 일 목록에 체크표시 추가하기

두번째 챌린지에서는 expo icon 중 Fontisto 그룹에 든 checkbox-passive와 checkbox-active라는 아이콘을 사용했다.

아이콘을 클릭했을 때 변화가 일어나야 하므로 TouchableOpacity 컴포넌트와 그 props인 onPress를 이용할 것이다.

import { Fontisto } from "@expo/vector-icons";

//중략 및 일부 발췌 
//App.js 리턴문 내부에서 체크박스 관련 부분만 가져옴
 {toDos[key].done ? (
                  <TouchableOpacity onPress={() => changeDone(key)}>
                    <Fontisto
                      name="checkbox-active"
                      size={18}
                      color={theme.grey}
                    />
                  </TouchableOpacity>
                ) : (
                  <TouchableOpacity onPress={() => changeDone(key)}>
                    <Fontisto
                      name="checkbox-passive"
                      size={18}
                      color={theme.grey}
                    />
                  </TouchableOpacity>
                )}

위 코드를 보면 알 수 있듯이 done이라는 속성이 삼항연산자의 조건이 되어주고 있다. 먼저 toDos라는 객체 내부에 있는 각 할 일들에 done이라는 속성을 추가해주어야 한다. (참고로 toDos라는 큰 객체 내부에 id들이 key로 존재하고, 각 key마다 값으로 text, working, done이라는 키와 값들을 가진 객체를 가진다)

done이라는 속성 역시 boolean 값이다.

//addToDo는 TextInput 컴포넌트에서 입력받은 값을 새로운 할 일로 추가하고
//AsyncStorage에도 추가한 뒤
//setText 상태변경함수를 이용해 TextInput 값을 초기화하는 함수이다 
  const addToDo = async () => {
    if (text === "") {
      return;
    }
    const newToDos = Object.assign({}, toDos, {
      //아랫줄 코드와 같이 추가 
      [Date.now()]: { text, working, done: false },
    });
    setToDos(newToDos);
    await saveToDos(newToDos);
    setText("");
  };

icon 설명 아래에 있는 코드에 보면 onPress의 값으로 화살표 함수 내부에 changeDone이라는 함수가 있는 것을 알 수 있다.

changeDone은 done 속성을 업데이트 시켜주기위한 함수라고 생각하면 된다.

setToDos로는 할 일 리스트를 저장하고 있는 상태인 toDos를 업데이트하고, saveToDos를 통해 AsyncStorage에 저장된 값도 변경해준다.

  const changeDone = async (key) => {
    const newToDos = {
      ...toDos,
      [key]: { ...toDos[key], done: !toDos[key].done },
    };
    setToDos(newToDos);
    await saveToDos(newToDos);
  };




3. 할 일 목록에서 할 일을 수정할 수 있게 만들기

아직 완벽하게 해결하지 못한 챌린지라 일단 적어두기만 하고 이후 수정할 수도 있을 것 같다.

2번째 챌린지랑 크게 다르지 않은 방법을 사용했다.
toDos의 각 데이터마다 editting이라는 boolean값을 가진 속성을 추가해주었고, 이 editting이 true이면 TextInput 컴포넌트가 나타나고, false인 경우 Text 컴포넌트가 나타나도록 했다.

 {toDos[key].editting ? (
                <TextInput
                  style={styles.editInput}
                  onSubmitEditing={() => editToDoText(key)}
                  defaultValue={toDos[key].text}
                  onChangeText={onEditTextChange}
                  returnKeyType="done"
                />
              ) : (
                <Text
                  style={
                    toDos[key].done
                      ? {
                          ...styles.toDoText,
                          textDecorationLine: "line-through",
                        }
                      : styles.toDoText
                  }
                >
                  {toDos[key].text}
                </Text>
              )}

editting이라는 키의 값을 변경하는 것은 TouchableOpacity의 onPress와 changeEditting이라는 함수를 사용한다.

<TouchableOpacity onPress={() => changeEditting(key)}>
                    <Fontisto name="eraser" size={18} color={theme.grey} />
                  </TouchableOpacity>
  const changeEditting = async (key) => {
    const newToDos = {
      ...toDos,
      [key]: { ...toDos[key], editting: !toDos[key].editting },
    };
    setToDos(newToDos);
    await saveToDos(newToDos);
  };

지금 고민점들은 아래와 같다

  1. editting을 이용해 Text 컴포넌트를 TextInput 컴포넌트로 바꾸는 데까지는 성공했지만, TextInput에 입력되는 값을 새로운 useState 상태인 const [editText, setEditText] = useState("");을 사용하면 한 번에 하나의 수정 상태만 담게 된다. 여러 목록을 모두 수정하기를 누르고 각각 수정했을 때 상태가 섞이는 문제.

2.아무것도 새롭게 입력되지 않았을 때 키패드 done 버튼을 누르면 빈 Text 컴포넌트로 변경되는 문제.

2번은 방금 작성하다보니 그냥 if문을 통해 editText 값이 빈문자열이 아닐 때만 로직이 실행되도록 코드를 조금만 바꾸면 될 것 같다.

1번은 좀 더 고민해봐야겠다.

profile
키보드로 그려내는 일

0개의 댓글