ReactJS/개념정리

[REACT] Refactoring

xowoony 2023. 4. 19. 12:52

학습일 : 2023. 04. 19


지금부터는 할 일을 완료할 수 있는 기능을 추가해보고자 한다.
toDo의 상태를 바꿀 수 있어야 하니까 말이다.

클릭을 하면 TODO를 DOING이나 DONE으로 바꿀 수 있어야할 것이다.
(반대로도 가능해야 겠고.)

그 기능을 구현하고 나면 selectors라는 개념에 대해 공부해 볼 것이다.
selectors를 이용하면 state로부터 데이터를 만들 수 있다.


우선 수정할 부분이 있다.

코드를 이제 좀 간결하게 만들어 보기 위해
form 을 따로 빼서 components 폴더 밑에 CreateToDo.tsx 파일로 따로 분리시켜주고

li를 따로 빼서 ToDo.tsx로 분리,

atom도 atoms.tsx로 분리 하겠음.




<CreateToDo.tsx>

  toDos는 필요없다.
  수정말 할 수 있으면 되기 때문에 useSetRecoilState로 setToDos를 정의
  toDoState를 atom으로 부터 불러오게됨.

import { useForm } from "react-hook-form";
import { useSetRecoilState } from "recoil";
import { toDoState } from "./atoms";

interface IForm {
  toDo: string;
}

function CreateToDo() {
  const setToDos = useSetRecoilState(toDoState);
  const { register, handleSubmit, setValue } = useForm<IForm>();
  const handleValid = ({ toDo }: IForm) => {
    setToDos((oldToDos) => [
      { text: toDo, category: "TODO", id: Date.now() },
      ...oldToDos,
    ]);
    setValue("toDo", "");
  };
  return (
    <form onSubmit={handleSubmit(handleValid)}>
      <input
        {...register("toDo", {
          required: "please write a to do",
        })}
        placeholder="오늘 해야할 일을 입력하세요"
      />
      <button>add</button>
    </form>
  );
}

export default CreateToDo;

 



src 폴더에
atoms.tsx 파일 생성한 후 

 

 


<atoms.tsx>

ToDoList.tsx에 있던

IToDo 인터페이스와 toDoState atom을 짤라서 붙여넣어준다.

그리고 앞에 export를 붙여넣어줌.

import { atom } from "recoil";

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

export const toDoState = atom<IToDo[]>({
  key: "toDo",
  default: [],
});

 




이렇게 하면 CreateToDo에서 toDoState를 사용할 수 있게 된다.

 

 

 


<CreateToDo.tsx>
  수정말 할 수 있으면 되기 때문에 useSetRecoilState로 setToDos를 정의
 이젠 toDoState를 앞에서 따로 빼준 ataom으로 부터 불러오게됨.
IForm은 이곳에서만 쓰기 때문에 똑같이 ToDoList.tsx 에 있는 걸 복붙 해서 넣어주고
이 타입을 useForm에 적용시켜준다. useForm<IForm>();

import { useForm } from "react-hook-form";
import { useSetRecoilState } from "recoil";
import { toDoState } from "./atoms";

interface IForm {
  toDo: string;
}

function CreateToDo() {
  const setToDos = useSetRecoilState(toDoState);
  const { register, handleSubmit, setValue } = useForm<IForm>();
  const handleValid = ({ toDo }: IForm) => {
    setToDos((oldToDos) => [
      { text: toDo, category: "TODO", id: Date.now() },
      ...oldToDos,
    ]);
    setValue("toDo", "");
  };
  return (
    <form onSubmit={handleSubmit(handleValid)}>
      <input
        {...register("toDo", {
          required: "please write a to do",
        })}
        placeholder="오늘 해야할 일을 입력하세요"
      />
      <button>add</button>
    </form>
  );
}

export default CreateToDo;



두번째로는, ul 태그 밑에 map으로 li 를 만들어 준 것도 별개의 컴포넌트로 분리하겠다.
역시나 components 폴더 밑 ToDo.tsx 파일로 분리시켜 주도록 한다.

 

 


<ToDo.tsx>

이제 여기서 key는 필요하지 않기 때문에 삭제해준다.
text prop은 반드시 필요하기 때문에
ToDo({text}) 로 전달해주고
좋은 소식은 이미 toDo 타입을 만들었다는 것임 atoms.tsx에 있음
따라서 TODO({text}:IToDo) 라고 적어주고 import 하면 끝난다.

import { IToDo } from "./atoms";

function ToDo({text}:IToDo) {
    return <li>{text}</li>;
}

export default ToDo;

 




<ToDoList.tsx>

 {...toDo} 이렇게 써줌으로 인해

prop으로 text, category, 등등

필수적으로 써주어야 하는 prop들을 써주어야 하는 노고가 줄어듦

import { useRecoilValue } from "recoil";
import CreateToDo from "./CreateToDo";
import { toDoState } from "./atoms";
import ToDo from "./ToDo";


function ToDoList() {
  // 여기서는 useRecoilValue 값만 반환해주고 modifier 함수는 반환할 필요 없음
  const toDos = useRecoilValue(toDoState);

  return (
    <div>
      <h1>Thorn To Do</h1>
      <hr />
      {/* form 태그가 있던 자리 - CreateToDo.tsx로 따로 빼줬음*/}
      <CreateToDo />
      <ul>
        {toDos.map((toDo) => (
          // li가 있던 자리 - ToDo.tsx로 감
          <ToDo {...toDo}/>
        ))}
      </ul>
    </div>
  );
}

export default ToDoList;

 



코드를 정리해준 파일은 다음과 같다.


<ToDoList.tsx>

import { useRecoilValue } from "recoil";
import CreateToDo from "./CreateToDo";
import { toDoState } from "./atoms";
import ToDo from "./ToDo";


function ToDoList() {
  // 여기서는 useRecoilValue 값만 반환해주고 modifier 함수는 반환할 필요 없음
  const toDos = useRecoilValue(toDoState);

  return (
    <div>
      <h1>Thorn To Do</h1>
      <hr />
      {/* form 태그가 있던 자리 - CreateToDo.tsx로 따로 빼줬음*/}
      <CreateToDo />
      <ul>
        {toDos.map((toDo) => (
          // li가 있던 자리 - ToDo.tsx로 감
          <ToDo {...toDo}/>
        ))}
      </ul>
    </div>
  );
}

export default ToDoList;




<CreateToDo.tsx>

import { useForm } from "react-hook-form";
import { useSetRecoilState } from "recoil";
import { toDoState } from "./atoms";

interface IForm {
  toDo: string;
}

function CreateToDo() {
  // toDos는 필요없다.
  // 수정말 할 수 있으면 되기 때문에 useSetRecoilState로 setToDos를 정의
  // toDoState를 ataom으로 부터 불러오게됨.
  const setToDos = useSetRecoilState(toDoState);
  const { register, handleSubmit, setValue } = useForm<IForm>();
  const handleValid = ({ toDo }: IForm) => {
    setToDos((oldToDos) => [
      { text: toDo, category: "TODO", id: Date.now() },
      ...oldToDos,
    ]);
    setValue("toDo", "");
  };
  return (
    <form onSubmit={handleSubmit(handleValid)}>
      <input
        {...register("toDo", {
          required: "please write a to do",
        })}
        placeholder="오늘 해야할 일을 입력하세요"
      />
      <button>add</button>
    </form>
  );
}

export default CreateToDo;




<atoms.tsx>

import { atom } from "recoil";

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

// CreateToDo에서 이 toDoState를 사용할 수 있게 됨.
export const toDoState = atom<IToDo[]>({
  key: "toDo",
  default: [],
});





<ToDo.tsx>

import { IToDo } from "./atoms";

function ToDo({text}:IToDo) {
    return <li>{text}</li>;
}

export default ToDo;

 

 

 

 


실행결과

li 추가되는 방향은 아래로 바꿨음

 

 

 

따로 파일을 분리시켜도 

정상적으로 작동이 잘 되는 것을 확인 할 수 있음