useState를 타이핑하는 방법

TypeScript에서 useState를 사용하기

Table Of Contents

들어가기

setState hook에서 set function을 반환받았다. set function을 하위 컴포넌트로 넘겨줄 때, 또또또 setState: any라고 쓰려다가 멈췄다. (아니면 setState: Function도 있다🤣) state를 타이핑하는 방법은 쉬운데, set function을 prop으로 넘겨줄 때 타입은 어떻게 해야 할까?

useState란?

useState란 React의 Hook 중 하나인데, state variable을 컴포넌트에 추가시킨다.

사용

useState는 아래와 같이 사용하면 된다.

const [state, setState] = useState(initialState);

initialState라는 값을 인자로 넘겨주면 JS의 Array Destructing 문법을 이용해서 state와 setState 함수에 값을 할당하는 구조이다.

예시

function Component() { const [inputString, setInputString] = useState(""); const [count, setCount] = useState(1); setInputString("test"); setCount(prevCount => prevCount + 1); }

TypeScript 파일 살펴보기

@types/react v18을 기준으로 작성되었습니다.

TypeScript에서는 아래와 같이 정의되어 있다.

function useState<S>( initialState: S | (() => S) ): [S, Dispatch<SetStateAction<S>>]; function useState<S = undefined>(): [ S | undefined, Dispatch<SetStateAction<S | undefined>> ];

매개변수가 있으면 첫 번째 오버로딩, 없다면 두 번째 오버로딩에 해당한다.

1. 매개변수(initialState)가 있는 경우

function useState<S>( initialState: S | (() => S) ): [S, Dispatch<SetStateAction<S>>];

2. 매개변수(initialState)가 없는 경우

function useState<S = undefined>(): [ S | undefined, Dispatch<SetStateAction<S | undefined>> ];

state 타이핑하기

이제 실제로 state 변수를 타이핑하는 방법을 알아보자. 이에 앞서 매개변수를 사용하는 경우와 사용하지 않는 경우의 예시를 먼저 보자.

// 1 const [count, setCount] = useState(1); // 2 const [mode, setMode] = useState("ready"); // 3 const [coords, setCoords] = useState({ x: 0, y: 0 }); // 4 const [value, setValue] = useState();

매개변수를 사용하는 경우 3가지와 사용하지 않는 경우 1가지를 가져왔다.

  1. 1번의 경우에는 매개변수로 1을 넘겨주고 있기 때문에 countnumber로 추론될 것이다. 하지만 number 타입임을 명시해 주기 위해서 아래와 같이 써도 된다.
const [count, setCount] = useState<number>(1);
  1. 2번의 경우에는 매개변수로 문자열 "ready"를 넘겨주고 있다. 따라서 modestring 타입으로 추론될 것이다. 하지만 여기서 리터럴 타입을 적용한다면 어떨까? 우선 아래와 같이 Mode라는 타입을 정의한다.
    type Mode = "ready" | "running" | "finished";
    다음으로는 1번처럼 해당 타입을 적용해주면 된다.
    const [mode, setMode] = useState<Mode>("ready");
  2. 이번에는 coords에 x와 y를 키로 가지는 object를 저장하고자 한다. 이 경우 역시 Coords같이 타입을 새로 정의하고 사용할 수 있다.
    type Coords = { x: number; y: number; }; const [coords, setCoords] = useState<Coords>({ x: 0, y: 0 });
  3. 매개변수가 없는 경우이다. 이렇게만 쓴다면 valueundefined로 추론되기 때문에, 사용할 타입에 맞게 타이핑해주어야 한다. 예를 들어서 boolean타입으로 사용하고 싶다면 아래처럼 쓸 수 있다.
    const [value, setValue] = useState<boolean>();

setState 함수 타이핑하기

setState 함수는 하위 컴포넌트로 내리는 일 이외에는 거의 타이핑할 일이 없다고 생각하기 때문에 컴포넌트로 예를 들어보겠다. 하지만 다른 경우에서도 똑같이 하면 된다.

1. 매개변수(initialState)가 있는 경우

import { Dispatch, SetStateAction } from 'react'; ... function SomeComponent({ setState, }: { setState: Dispatch<SetStateAction<number>> }) { ... }

number 타입 state를 변경하는 setState 함수는 이렇게 타이핑하면 된다. number 이외의 타입도 해당 자리에 적절히 써 주면 잘 작동한다. 위에서 봤듯이, Dispatch<SetStateAction<number>> 는 결국 (prevState: number | ((prevState: number) => number)) => void; 와 같기 때문에 이렇게 바꿔 써 주어도 잘 작동한다!

2. 매개변수(initialState)가 없는 경우

import { Dispatch, SetStateAction } from 'react'; ... function SomeComponent({ setState, }: { setState: ( prevState: Dispatch<SetStateAction<number | undefined>> ) => void; }) { ... }

undefined가 추가되는 것 말고 다른 점은 없다. 이 역시

prevState: | (number | undefined) | ((prevState: number | undefined) => number | undefined)

처럼 써 주어도 작동한다.

결론

useState<type>()

Dispatch<SetStateAction<type>>

만 기억하면 앞으로 TypeScript와 useState를 사용할 때 꽤 편리하게 코딩할 수 있을 것 같다!

참고

React docs - useState

타입스크립트 교과서(조현영 저)