React.js & TypeScript (state, ref, 함수타입)

강정우·2023년 2월 6일
0

TypeScript

목록 보기
3/22
post-thumbnail

event

  • form의 submit에서 받아오는 이벤트 객체에 대한 데이터 타입또한 매개변수이기에 지정을 해줘야하는데 onSubmit, onClick이 각각 따로 존재한다.
const submitHandler = (event:React.FormEvent) => {
const submitHandler = (event:React.MouseEvent) => {

  • 또한 위와 같이 onSubmit 속에 잘 못된 event 객체를 넣게 된다면 그 또한 에러의 대상이 된다.

ref

  • 우리는 그동안 사용자 입력을 state, value 또는 ref를 통하여 관리하였다.
    하지만 JS처럼 하면 안된다. 왜냐면 그동안은 타입 표기의 개념이 없었기 때문이다.

  • 따라서 타입스크립트로 작업할 때는 우리가 만든 레퍼런스에 대해 더 많은 정보를 알려줘야 한다.
    useRef로 레퍼런스를 만들 때 타입스크립트는 이 레퍼런스가 어떠한 요소에 연결될 거라는 사실을 알지 못하기 때문이다.

  • 또 해당요소의 ref에 레퍼런스를 주더라도 명확하게 이 HTML 요소에 맞는 레퍼런스가 아니게 되기 때문에 button 요소의 ref에도 넣을 수있고 무튼 애매하게 되버린다. 그래서 이 레퍼런스에 저장될 데이터가 어떤 타입인지 명확히 밝혀야한다.

  • 그래서 여기서 또 우리는 generic type을 사용한다.

const 사용자Ref = useRef<HTML엘리먼트>(null);
  1. generic안에 HTML element 타입 넣어주기
    • 예를들어 버튼: <HTMLButtonElement>, 입력: <HTMLInputElement>...
  2. default value 설정하기
    • 레퍼런스 요소의 다른 요소가 할당될 수 있기에 Ref가 생성되었을 때 자동으로 지정해주는 것.
    • 이때 input 값은 아예 텅 비어있으니 null

현재값 current.value

  • 우리는 그동안 current.value로 값을 가져왔는데 여기는 TS이다. TS에서는 input에 값이 들어있는지 않은 즉, null에 대한 error처리를 또 해줘야한다. 아래의 예제코드를 봐보자.
const submitHandler = (event:React.FormEvent) => {
  event.preventDefault();
  const enteredText = todoTextInputRef.current?.value;			// <= 이부분
  }
  return (
    <form onSubmit={submitHandler}>
      <label htmlFor={"text"}>Todo text</label>
      <input type={"text"} id={"text"} ref={todoTextInputRef}/>
      <button>Add Todo</button>
    </form>
  );
  • 여기서 우리가 물음표를 추가한 이유는 input값을 사용하려는 시점에 레퍼런스에 아직 값이 설정되지 않았을 수도 있기 때문이다.

  • 물론 우리는 submitHandler가 호출되는 시점에 todoTextInputRef가 요소와 연결된다는 걸 알지만 TS는 알 길이 없다.
    그래서 추가된 물음표가 타입스크립트에게 일단 값에 접근해보고 접근이 가능하다면, 그때 입력된 값을 가져와 enteredText 저장하라고 지시하는 것이다.
    만약 접근이 불가능하면 아직 연결되지 않은 상태일 거고 null이 enteredText에 저장된다.

  • 그래서 enteredText의 추론된 타입이 string 또는 undefined로 나타나는 것이다.
    이는 타입스크립트가 실제 value를 가져올 수 있는지 반드시 알아야 할 필요는 없다는 뜻이다

  • 하지만 우리는 이 시점에는 값이 null이 아니라는 것을 알기 때문에 즉, 레퍼런스와 요소가 연결되었다는 걸 알기 때문에 물음표 대신 느낌표를 사용할 수 있다

  • 이 특수 기호는 타입스크립트에게 이 시점에는 절대 null이 아니라고 알려주는 것이다.
    따라서 null이 아니라는 걸 100% 확신하는 경우에만 사용해야 한다.

  • 이렇게 하면 추론된 타입에 string만 나온다.

null일 수도 있는 값을 다룰 때는 ? 를 사용해서 먼저 해당 값에 접근해보고 null일 경우, 상수 또는 값을 저장할 변수에 null을 저장하고
! 를 사용하면 이 위치에서는 해당 값이 절대 null이 될 수 없으니 바로 객체의 프로퍼티로 들어가서 null이 아닌 실제 값을 가져오라는 뜻이다.

함수타입

  • 만약 우리가 자식=>부모로 데이터를 넘길 때 보통 on000라고 하여 부모=>자식 컴포넌트로 보낸다음 자식 컴포넌트에서 로직을 거쳐 나올 결과값을 props.on000( )로 하여 메서드에 담에 올렸다. 이때 이것을 TS로 구현을 하려면 props에서 넘어온 props.on000( )를 데이터 타입으로 지정해주어야한다.

  • 바로 이를 ‘함수 타입’이라 불리는 타입으로 지정해야 한다.

사용법

const NewTodo: React.FC<{ onAddTodo: (text: string) => void }> = (props) => {
  • 단순히 화살표 함수를 사용하면 된다.

  • 화살표 다음에는 이 함수의 반환 타입(리턴 타입)을 정의하면 되는데
    여기의 onAddTodo는 값을 반환할 필요가 없다.
    그 이유는 props객체의 속성중 하나인 매서드가 실행되었는데 그 메서드가 반환값이 없다면 void로 하는 것이 맞다.

  • 코드를 보면 더 이해가 쉬운데

    const submitHandler = (event: React.FormEvent) => {
        event.preventDefault();
        const enteredText = todoTextInputRef.current!.value;
        if (enteredText.trim().length === 0) {
            // throw an error
            return;
        }
        props.onAddTodo(enteredText);				// <= 이 부분
    };
  • 위의 NewTodo 컴포넌트 함수의 일부이다. props.프롭함수(매개변수) 이 부분을 그냥 데이터 타입으로 변환한 후 위의 함수타입안에 각각 넣어주면 된다.

  • 그리고 당연히 부모 컴포넌트에서는 "프롭함수" 와 완전 동일한 데이터 타입의 위치와 조건으로 선언이 되어있어야 한다.

즉, 부모컴포넌트의 프롭함수 정의의 데이터 타입 === 부모 컴포넌트 => 자식컴포넌트의 속성 === 자식 컴포넌트의 함수타입
요 3개가 3위일체가 되어야 한다.

useState

  • 우리는 그동안 react에서 어떠한 변경된 화면을 보고싶다면 state로 항상 관리를 해왔다. 여기 TS에서 state를 사용하는 법을 알아보자.
const [state변수, setstate변수] = useState<데이터타입>(init데이터);

  • 우선 state의 반환값중 2번째 index에 있는 set함수를 봐보자 Dispatch라고 적혀있다.
  • 이 타입은 상태 업데이트 함수가 갖는 타입으로 이 함수를 호출해 state update를 요청하여 state를 변경할 수 있다

  • useState([ ]) 만약 이렇게 초기화를 해버린다면 객체state는 항상 비어있어야한다고 판단한다.
    이렇게 되면 어떠한 값도 추가할 수 없다.
  • 그렇다면 어떻게 이 빈 배열에 값이 추가될 수 있다고 TS에게 알려줄까?
  • 사실 useState또한 generics함수이다. 그래서 또 옆에 <> 을 추가하여 데이터타입을 지정할 수 있다.

예제코드

const [todos, setTodos] = useState<Todo[]>([]);			// 1.

const addTodoHandler = (todoText: string) => {			// 2.
  const newTodo = new Todo(todoText);			// 3. 
  setTodos((prevState)=>{						// 4.
    return prevState.concat(newTodo);			// 5.
  })
};
  1. return값을 Todo 객체 타입으로 이루어진 배열을 반환하겠다고 state를 설정하였고 빈 배열로 초기화를 진행하였다.
  2. 자식 컴포넌트에서 받은 값을 매개변수로 담았다.
  3. state에서 선언했듯 Todo 객체로 이루어진 배열을 반환하기 위해 Todo객체를 인스턴스화하여 변수에 담아줬다.
  4. 최신 state를 보장받기 이전 state를 기반으로 state를 업데이트하였다.
  5. concat를 사용하여 "새로운" 배열을 생성하여 반환하였다.
    • "새로운"은 데이터 타입이 참조타입이여서 붙였다.
profile
智(지)! 德(덕)! 體(체)!

0개의 댓글