[TIL]React/Typescript Recoil&Form을 이용한 리스트 만들기

ohoho·2024년 10월 29일

슬기로운스터디

목록 보기
38/54

오늘 공부한것 & 기억할 내용

Recoil과 Form을 이용한 리스트만들기

1.atom.ts

import { atom, selector } from 'recoil';
//TodoData에 사용할 interface
export interface IToDo {
  text: string;
  id: number;
  category: 'NONE' | 'VISIT' | 'LIKE';
}
export const TodoData = atom<IToDo[]>({
  key: 'ToDo',
  default: [],
});

//TodoData의 원본값을 변경하지 않고 원하는 형태의 값들만 리턴한다.
export const TodoSelector = selector({
  key: 'TodoSelector',
  get: ({ get }) => {
    const getData = get(TodoData);
    //리턴된 값들을 []배열안에 객체형식으로 담아준다.
    return [
      getData.filter((i) => i.category === 'NONE'),
      getData.filter((i) => i.category === 'VISIT'),
      getData.filter((i) => i.category === 'LIKE'),
    ];
  },
});

2.CreateTodo.tsx

interface ITodoData {
  [key: string]: string;
}
export default function CreateTodo() {
  //TodoData의 값 변경위해 useSetRecoilState 사용
  const setTodo = useSetRecoilState(TodoData);
  //Form에서 사용할것들 useForm사용해 꺼내준다
  const {
    register,
    handleSubmit,
    formState: { errors },
    setValue,
  } = useForm<ITodoData>();
  //Form의 onSubmit에 들어갈 이벤트
  const onValid = (data: ITodoData) => {
    //input값을 빈값으로 초기화
    setValue('todo', '');
    //input값을 객체 형식으로 담아 TodoData에 넣어준다
    //TodoData의 기본값은 배열이니 배열에 담고 ...pre를 사용해 기존 배열을 복사한다.
    setTodo((pre) => [
      { text: data?.todo, id: Date.now(), category: 'NONE' },
      ...pre,
    ]);
  };
  return (
    <>
      <Form onSubmit={handleSubmit(onValid)}>
        <input
          type="text"
          placeholder="이름"
          {...register('todo', {
            required: '😠 required!',
          })}
        />
		//input이 빈값일때 required에 해당하는 에러 메시지 노출
        <span>{errors?.todo?.message}</span>
        <button>가자</button>
      </Form>
    </>
  );
}
  1. Todo.tsx
import { IToDo, TodoData } from '../atom';

export default function Todo({ text, id, category }: IToDo) {
  //useSetRecoilState를 사용하여 TodoData의 state를 바꿀 변수 선언
  const setToDos = useSetRecoilState(TodoData);

  //삭제될 Todo Event
  const onDelete = () => {
    //filter를 사용하여 기존 배열에 있는 id와 현재 id 비교하여 같지 않는것들만 남김
    setToDos((prev) => prev.filter((todo) => todo.id !== id));
  };

  //button클릭시 category바뀔 이벤트 생성
  //button클릭시 들어오는 인자를 받는다(newCategory)
  const onChangeCategory = (newCategory: IToDo['category']) => {
    setToDos((prev) => {
      //기존 data 배열의 id와 현재 Todo의 id가 일치하면 맞는 index반환
      const targetIdx = prev.findIndex((todo) => todo.id === id);
      //업데이트될 함수
      const updatedTodo = { text, id, category: newCategory };
      //기존 배열의 앞부분과 뒷부분 사이에 updatedTodo를 넣으면 배열의 순서가 바뀌지 않고 
      //업데이트 된 부분만 변경된다.
      return [
        ...prev.slice(0, targetIdx),
        updatedTodo,
        ...prev.slice(targetIdx + 1),
      ];
    });
  };

  //카테고리별로 button개수&내용 변경
  return (
    <li>
      <span>{text}</span>
      {category === 'NONE' && (
        <>
          <button onClick={() => onChangeCategory('VISIT')}></button>
          <button onClick={onDelete}>🗑️</button>
        </>
      )}
      {category === 'VISIT' && (
        <>
          <button onClick={() => onChangeCategory('LIKE')}>👍🏻</button>
          <button onClick={() => onChangeCategory('NONE')}></button>
        </>
      )}
      {category === 'LIKE' && (
        <button onClick={() => onChangeCategory('VISIT')}>👎🏻</button>
      )}
    </li>
  );
}
  1. TodoList.tsx
export default function TodoList() {
  //TodoSelector에서 나눠 놓은 배열을 순서대로 이름을 지정해준다.
  const [none, visit, like] = useRecoilValue(TodoSelector);
  return (
    <>
      <h1>내가 가고싶은 나라들</h1>
      <CreateTodo />
      <ul>
        {none.map((i) => (
          <Todo key={i.id} {...i} />
        ))}
      </ul>
      <h1>내가 가본 나라들</h1>
      <ul>
        {visit.map((i) => (
          <Todo key={i.id} {...i} />
        ))}
      </ul>
      <h1>내가 좋아하는 나라들</h1>
      <ul>
        {like.map((i) => (
          <Todo key={i.id} {...i} />
        ))}
      </ul>
    </>
  );
}

결과

배운점 & 느낀점

recoil의 selector기능이 내가 원하는 data들 값만 정리해서 사용하기 좋았고, Form을 사용하니 useForm하나만 선언해서 여러 기능들을 꺼내 쓸 수 있어서 좋았다.
이번 코드 챌린지를 하면서 단순한 ToDoList가 아닌 내가 지정한 카테고리 별로 이동을 하는부분에서 꽤 애를 먹었다.
버튼의 내용,기능,개수 다른부분에서도 어떻게 구현해야할지 머리가 아팠지만 구글링과 주변의 조언을 통해 나름 간단히 해결한거 같아 뿌듯하다.

0개의 댓글