aotoyae

[TS/React] TodoList 에 활용, props & children props 본문

TypeScript

[TS/React] TodoList 에 활용, props & children props

aotoyae 2024. 3. 6. 12:08

 

 

💡 타입스크립트를 투두리스트에 활용해 보자.

 

type Todo = {
  id: string;
  isDone: boolean;
};

function App() {
  const [todos, setTodos] = useState<Todo[]>([]); // Todo 에 세팅은 어떻게 넘길까?!

  return (
    <>
      {todos.map(({ id }) => (
        <Todo key={id} />
      ))}
    </>
  );
}

function Todo() {
  return <div></div>;
}

export default App;

vscode 가 친절하게 알려준다.

function App() {
  const [todos, setTodos] = useState<Todo[]>([]);

  return (
    <>
      {todos.map(({ id }) => (
        <Todo key={id} id={id} setTodos={setTodos} /> // ❗️ 프롭스 넘겨주고
      ))}
    </>
  );
}

function Todo({
  id,
  setTodos,
}: {
  id: string;
  setTodos: React.Dispatch<React.SetStateAction<Todo[]>>; // ❗️ 요렇게 타입 지정
}) {
    const deleteTodo = () => {
    setTodos((prev) => prev.filter((todo) => todo.id !== id)); // ❗️ 잘 사용 가능
  };

  return <div onClick={deleteTodo}></div>;
}

 

❗️ 위 코드가 너무 복잡해보인다면, setTodos 타입은 아래처럼 쓸 수도 있다.

function Todo({
  id,
  setTodos,
}: {
  id: string;
  setTodos: (cb: (todo: Todo[]) => Todo[]) => void;
})

 

setTodos: () => void;

setTodos 는 인자를 받아 아무것도 반환하지 않는 함수라고 정의 (setTodos 는 함수다!)

 

setTodos: (cb: (todo: Todo[]) => Todo[]) => void;

인자로 콜백함수를 받는데 그 콜백함수는 todo 리스트를 받아 todo 리스트로 반환한다.

 

setTodos((prev) => prev.filter((todo) => todo.id !== id)); ↖️

아래 쓴 이 부분을 보고 어떻게 활용되는지 타입을 적어준 것!

 

❗️ 더 간단하게 세팅만 하고 싶다면?

function App() {
  const [todos, setTodos] = useState<Todo[]>([]);

  return (
    <>
      {todos.map(({ id }) => (
        <Todo key={id} id={id} todos={todos} setTodos={setTodos} />
      ))}
    </>
  );
}

function Todo({
  id,
  todos, // 투두스를 받아오고
  setTodos,
}: {
  id: string;
  todos: Todo[]; // ** 투두스는 투두 배열이라고 정의
  setTodos: (todoList: Todo[]) => void; // ❗️ 인자를 간단하게 적어준다. 투두리스트: 투두배열
}) {
  const deleteTodo = () => {
    const newTodos = todos.filter((todo) => todo.id !== id); // ❗️ 뉴투두스를 정해주고
    setTodos(newTodos); // ❗️ 세팅!
  };

  return <div onClick={deleteTodo}></div>;
}

 

❗️ setTodos 를 통째로 넘기는게 아니라,
deleteTodo 같은 함수를 부모에서 만들고 넘기고 싶다면?

function App() {
  const [todos, setTodos] = useState<Todo[]>([]);

  const deleteTodo = (id: string) => { // ❗️ deleteTodo 함수를 위로 가져오고
    const newTodos = todos.filter((todo) => todo.id !== id);
    setTodos(newTodos);
  };

  return (
    <>
      {todos.map(({ id }) => (
        <Todo key={id} id={id} deleteTodo={deleteTodo} /> // ❗️ 넘겨준다.
      ))}
    </>
  );
}

function Todo({
  id,
  deleteTodo,
}: {
  id: string;
  deleteTodo: (id: string) => void; // ❗️ 인자에는 위에 적었듯이 id 만!
}) {
  const handleOnClick = () => {
    deleteTodo(id); // ❗️ 실행할 땐 이렇게
  };

  return <div onClick={handleOnClick}></div>;
}

어떤 함수인지 잘 나온다. 모를 땐 봐보기 👀

 

💡children props

❗️FC<>

React 18 이전 버전에서의 작성법 ~ 참고로 알아두자.

type BaseType = {
  id: string;
};

const Child: React.Fc<BaseType> = ({ id }) => { // ❗️ 암묵적으로 타입 전달 중
  return <div>{id}</div>;
};

export function Parent() {
  return (
    <Child id="">
      <div>has children</div> // ❗️ 암묵적으로 타입 전달 받음
    </Child>
  );
}

 

❗️PropsWithChildren<>

명시적인 친구, 이름도 칠드런으로 프롭스 만듭니다 ~

import { PropsWithChildren } from 'react';

type BaseType = {
  id: string;
};

function Child({ children }: PropsWithChildren<BaseType>) {
  return <div>{children}</div>;
}

export function Parent() {
  return (
    <Child id="">
      <div>has children</div>
    </Child>
  );
}

그런데 저 친구를 살펴보니 childre? 하고 두가지 값이 들어가 있다.

 

요건 아래처럼 값이 있으나 없으나 오류를 안뱉는다는 이야기 ➡️ 그래서 얘도 덜 명시적이란 느낌이 있다.

 

❗️StrictChildren<T> = T & {children : ReactNode}

더 엄격한 아이 ~~ StrictChildren 은 아무렇게나 지어도 된다.

 T & {children : ReactNode}

➡️ 제네릭으로 타입을 넣고 and children 에 | undefined 가 아니라 ReactNode 로 확실히 지정

import { ReactNode } from 'react';

type BaseType = {
  id: string;
};

type StrictChildren<T> = T & { children: ReactNode };

function Child({ children }: StrictChildren<BaseType>) {
  return <div>{children}</div>;
}

export function Parent() {
  return (
    <Child id="">
      <div>has children</div>
    </Child>
  );
}

그래서 값이 없으면 오류 뜬다.