[ReactJS_MasterClass] #6 STATE MANAGEMENT (2)

유은선·2023년 2월 2일
0

ReactJS_MasterClass

목록 보기
7/11
post-thumbnail

✍6.11 Add To Do

TODO interface를 만드는데, 명시된 string 만을 가질 수 있다고 설정 해줄 수 있다.

interface IToDo {
  text: string;
  category: "TODO" | "DOING" | "DONE";
}

✍6.12 Refactoring

CreateToDo, ToDo, ToDoList로 컴포넌트를 분리

key 에러를 해결하기 위해 ToDo에 키 값만 주면 된다.

{toDos.map((toDo) => (
          <ToDo key={toDo.id} {...toDo} />
        ))}

✍6.13 Categories

✅ 1. 새 익명함수를 만들어 인자를 전달하는 방법

<button onClick={onClick}>Doing</button>
<button onClick={() => onClick("DOING")}>Doing</button>

첫번째 코드처럼 작성해도 작동은 하지만 인자가 넘겨지지 않으므로 두번째 코드로 익명 함수를 써서 인자를 넘겨주도록 한다.

 const onClick = (newCategory: IToDo["category"]) => {
    console.log("I wanna to ", newCategory);
  };
  
{category !== "DOING" && (
        <button onClick={() => onClick("DOING")}>Doing</button>
      )}

✅ 2. 버튼에 각각 name을 지정해주고 인자 없이 함수 쓰기

const onClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    const {
      currentTarget: { name },
    } = event;
  };
  
  {category !== "DOING" && (
        <button name="DOING" onClick={onClick}>
          Doing
        </button>
      )}

newCategory: IToDo["category"]
== newCategory: "TODO" | "DOING" | "DONE"

✍6.14 Immutability part One

todo의 category를 수정해보자💫
✅1. 수정하고자 하는 todo의 경로를 알아야한다.

const targetIndex = oldToDos.findIndex((toDo) => toDo.id === id);
console.log(targetIndex);

findIndex는 조건을 만족하는 todo의 index를 찾아준다.

✅2. 새로운 todo를 만들어서 원래의 todo를 update. 즉 targetIndex에 있는 todo를 newToDo로 바꿔주면 된다. (6.15에서 계속)

✍6.15 Immutability part Two

["pizza", "mango", "kimchi", "kimbab"] 배열에서 "mango"를 "apple"로 교체해보자.

교체하는 순서는 다음과 같다.
✅1. mango index 구하기
✅2. 배열을 두 부분으로 나누기

const food = ["pizza", "mango", "kimchi", "kimbab"];
const front = ["pizza"];
const back = ["kimchi", "kimbab"];
const finalPart = [...front, "apple", ...back];

front는 "mango" 이전 원소의 배열이고, back은 모든 "mango" 이후 원소의 배열이다. ...은 배열 안에 있는 모든 원소를 풀어놓는다는 의미인데, ...를 쓰지 않으면 배열안에 배열을 넣게 된다.

콘솔창에 이 과정을 써보는 연습을 해보자.

setToDos((oldToDos) => {
      const targetIndex = oldToDos.findIndex((toDo) => toDo.id === id);
      const newToDo = { text, id, category: name  };
      return [
        ...oldToDos.slice(0, targetIndex),
        newToDo,
        ...oldToDos.slice(targetIndex + 1),
      ];
    });

완성된 코드는 다음과 같고, 이렇게 작성하면 oldToDos에 오류가 뜨는데 category라는 prop의 타입이 호환되지 않기 때문이다. category는 "TODO", "DOING", "DONE" 중 하나여야 하는데 newToDo의 category는 그냥 string 여서 문제가 나는 것🤣
이 문제를 회피 하기 위해 타입스크립트에게 체크하지 말라고 as any를 쓰면 되는데 별로 좋은 방법은 아니여서 6.13 의 1번 방법을 쓰는 게 더 나을 듯 하다.

const newToDo = { text, id, category: name as any };

따라서 원래의 toDo를 업데이트 하는 것이 아니라, 원래 있던 toDo를 지우고 새 배열을 만드는 것!

✍6.16 Selectors part One

지금까지는 toDoState에 "DOING", "TODO", "DONE"을 구별하지 않고 모든 todo를 담고있다. selector를 이용해서 이 todo들을 구별해보자.

정리하자면, selector는 atom의 output을 변형시키는 도구이다.
selector는 key와 get function이 필요한데, get function은 인자로 객체를 받고, 그 객체에는 get function이 들어가 있는데 이것을 이용하면 selector의 내부로 atom을 가지고 올 수 있다.

export const toDoSelector = selector({
  key: "toDoSelector",
  get: ({ get }) => {
    const toDos = get(toDoState);
    return [
      toDos.filter((toDo) => toDo.category === "TODO"),
      toDos.filter((toDo) => toDo.category === "DOING"),
      toDos.filter((toDo) => toDo.category === "DONE"),
    ];
  },
});

✍6.17 Selectors part Two

6.15에서는 한 곳에 데이터를 몰아놓고 컴포넌트 안에서 수정하는 대신, atom에 데이터를 모아두고 selector로 데이터를 변형하여 코드를 작성해 보았다. 6.16에서는 다른 방식을 써보기로 하자

✅1. 사용자가 현재 선택한 카테고리를 저장하는 state를 만들기

 const onInput = (event: React.FormEvent<HTMLSelectElement>) => {
    console.log(event.currentTarget.value);
  };

✅2. value를 category state atom과 연결
이제 ToDoList.tsx에서 이차원배열이 아닌 일차원배열 하나만 가져오게 된다. 모든 처리는 selector에서 할 것인데, category에 따라서 selector가 각각의 toDo 배열을 반환한다.

//ToDoList.tsx
 const toDos = useRecoilValue(toDoSelector);
 
 {toDos?.map((toDo) => (<ToDo key={toDo.id} {...toDo} />))}
//atom.tsx
export const toDoSelector = selector({
  key: "toDoSelector",
  get: ({ get }) => {
    const toDos = get(toDoState);
    const category = get(categoryState);
    return toDos.filter((toDo) => toDo.category === category);
  },
});

selector가 toDos와 category를 받아서 category에 따라 toDo를 분류해준다.

현재는 Done 카테고리에 있어도 toDo를 추가하면 바로 나타나지 않는다. 자동으로 "TODO"리스트에 들어가는데, 다음 챕터에서는 toDo를 추가할 때 지금 category에 바로 넣을 수 있도록 해보자.

✍6.18 Enums

지금은 새 toDo를 추가할 때, 매번 "TODO" 카테고리로 들어가게 된다. 이번 챕터에서는 toDo의 카테고리가 categoryState에 따라서 추가되게 해보자!

//CreateToDo.tsx
const category = useRecoilValue(categoryState);

const onSubmit = ({ toDo }: IForm) => {
    setToDos((oldToDos) => [
      { text: toDo, id: Date.now(), category: category },
      ...oldToDos,
    ]);
    setValue("toDo", "");
  };

//atom.tsx
type categories = "TODO" | "DOING" | "DONE";

export const categoryState = atom<categories>({
  key: "category",
  default: "TODO",
});

//ToDoList.tsx
const onInput = (event: React.FormEvent<HTMLSelectElement>) => {
    setCategory(event.currentTarget.value as any);
  };

CreateToDo.tsx에서의 category:categorycategory로 써도 된다.
이렇게 쓰면 에러가 나는데, atom.tsx에서 categoryState 타입을 써주면 에러가 해결된다.
또한 ToDoList.tsx에서 setCategory에서도 오류가 나는 것을 볼 수 있는데 이유는 setCategory 함수를 호출 할때 인자로 타입이 string인 값을 넘기고 있기 때문이다.

이 문제를 해결하려면 as any를 적어주면 된다. 타입스크립트가 보기에 option의 value는 그냥 string 이기 때문!


위에서 썼던 type은 복붙을 안하게 해주는 단순한 문법인데 충분하지 않다. 이제는 실수를 방지하기 위해서 코드 전체에서 "TODO", "DOING", "DONE"을 각각 사용할 수 있도록 해보자
export enum Categories {
  "TODO",
  "DOING",
  "DONE",
}
export const categoryState = atom<Categories>({
  key: "category",
  default: Categories.TODO,
});

ToDo.tsx의 name에서 오류가 나는데, 그 전에 확인할 내용이 있다.

이렇게 enum은 개발자의 코딩을 쉽게 해주는 도구로써 숫자임을 알 수 있다.

export const categoryState = atom<Categories>({
  key: "category",
  default: Categories.TODO , // default:0 
});

즉, Categories.TODO0과 같다.

원한다면 enum에서의 타입을 바꿀 수 있다.

export enum Categories {
  "TODO"="TODO",
  "DOING"="DOING",
  "DONE"="DONE",
}

위처럼 작성하면 TODO는 값은 실제로 숫자가 아니며 "TODO"가 될 것이다.

profile
뭐든지 난 열심히 하지

0개의 댓글