[React with TypeScript] 타입스크립트로 리액트 상태관리하기

📄 타입스크립트로 리액트 상태관리하기

타입스크립트를 사용한 프로젝트에서 리액트 상태관리 훅을 사용하는 경우가 있습니다.

📄 1. useState 타입스크립트로 관리하기

useState에 타입스크립트를 적용한 기본 구조는 다음과 같습니다.

// useState 타입스크립트 적용 기본 구조
useState<state의 type>(state 초기값);

// useState 예시 (카운터 예제)
const [count, setCount] = useState<number>(0);

이와 같이 Generics를 사용하여 상태의 타입을 설정합니다.

하지만 Generics를 사용하지 않아도 알아서 타입유추를 해주기 때문에 일일이 사용할 필요는 없습니다.

꼭 사용해야 하는 경우는 상태가 null인 경우일 때입니다.

type Information = { name: string; description: string };
const [info, setInformation] = useState<Information | null>(null);

▪ useState를 적용한 코드에서 인풋상태 관리하기

타입스크립트를 적용한 코드는 각각 어떤 타입을 사용하는지 쉽게 알아낼 수 있습니다.

타입이 궁금한 값에 마우스를 갖다 대기만 하면 타입을 알려주기 때문입니다.

// src/MyForm.tsx

import React, { useState } from "react";

type MyFormProps = {
  onSubmit: (form: { name: string; description: string }) => void;
};

function MyForm({ onSubmit }: MyFormProps) {
  const [form, setForm] = useState({
    name: "",
    description: "",
  });

  const { name, description } = form;

  const onChange = (e: any) => {
    // 타입을 모를 때는 any로 설정합니다. 하지만 any타입은 최대한 지양해야합니다.
  };

  const handleSubmit = (e: any) => {};

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" value={name} onChange={onChange} />
      <input name="description" value={description} onChange={onChange} />
      <button type="submit">등록</button>
    </form>
  );
}

export default MyForm;

위 코드의 input태그의 onChange에 마우스 커서를 올려두면 타입스크립트가 타입을 알려줍니다..

image

onChange의 타입은 Reaact.ChangeEventHandler<HTMLInputElement> 또는 undefined라고 알려주고 있습니다..

🚨 주의할 점!!

여기서 주의할 점은 저 타입을 그대로 가져다 쓰면 에러가 난다는 것입니다.

image

e.target에서 에러가 발생합니다.

타입 위에 Handler가 붙어 있어 에러가 발생했습니다.

Handler를 제거하고 React.ChangeEvent<HTMLInputElement>로 타입을 지정해 에러가 발생하지 않게 작성해야 합니다.

onChange와 마찬가지로 handleSubmit의 타입을 지정해줍니다.

import React, { useState } from "react";

type MyFormProps = {
  onSubmit: (form: { name: string; description: string }) => void;
};

function MyForm({ onSubmit }: MyFormProps) {
  const [form, setForm] = useState({
    name: "",
    description: "",
  });

  const { name, description } = form;

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setForm({
      ...form,
      [name]: value,
    });
  };

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    onSubmit(form);
    setForm({
      name: "",
      description: "",
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" value={name} onChange={onChange} />
      <input name="description" value={description} onChange={onChange} />
      <button type="submit">등록</button>
    </form>
  );
}

export default MyForm;

📄 2. useReducer 타입스크립트로 관리하기

useReducer를 사용하면 action을 타입으로 만들어주고 타입의 액션을 OR연산자를 사용하여 나열합니다.

type Action = { type: "INCREASE" } | { type: "DECREASE" };

액션 객체를 생성할 때 필요한 값을 타입안에 명시해주면 추후 리듀서를 사용할 때 자동완성이 되며 dispatch할때 타입명시도 해주어 코드작성에 편리합니다.

// useReducer 사용 예시

import React, { useReducer } from "react";

type Color = "red" | "orange" | "yellow";

type State = {
  count: number;
  text: string;
  color: Color;
  isGood: boolean;
};

type Action =
  | { type: "SET_COUNT"; count: number }
  | { type: "SET_TEXT"; text: string }
  | { type: "SET_COLOR"; color: Color }
  | { type: "TOGGLE_GOOD" };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case "SET_COUNT":
      return {
        ...state,
        count: action.count, // count가 자동완성되며, number 타입인걸 알 수 있습니다.
      };
    case "SET_TEXT":
      return {
        ...state,
        text: action.text, // text가 자동완성되며, string 타입인걸 알 수 있습니다.
      };
    case "SET_COLOR":
      return {
        ...state,
        color: action.color, // color 가 자동완성되며 color 가 Color 타입인걸 알 수 있습니다.
      };
    case "TOGGLE_GOOD":
      return {
        ...state,
        isGood: !state.isGood,
      };
    default:
      throw new Error("Unhandled action");
  }
}

function ReducerSample() {
  const [state, dispatch] = useReducer(reducer, {
    count: 0,
    text: "hello",
    color: "red",
    isGood: true,
  });

  const setCount = () => dispatch({ type: "SET_COUNT", count: 5 }); // count 를 넣지 않으면 에러발생
  const setText = () => dispatch({ type: "SET_TEXT", text: "bye" }); // text 를 넣지 않으면 에러 발생
  const setColor = () => dispatch({ type: "SET_COLOR", color: "orange" }); // orange 를 넣지 않으면 에러 발생
  const toggleGood = () => dispatch({ type: "TOGGLE_GOOD" });

  return (
    <div>
      <p>
        <code>count: </code> {state.count}
      </p>
      <p>
        <code>text: </code> {state.text}
      </p>
      <p>
        <code>color: </code> {state.color}
      </p>
      <p>
        <code>isGood: </code> {state.isGood ? "true" : "false"}
      </p>
      <div>
        <button onClick={setCount}>SET_COUNT</button>
        <button onClick={setText}>SET_TEXT</button>
        <button onClick={setColor}>SET_COLOR</button>
        <button onClick={toggleGood}>TOGGLE_GOOD</button>
      </div>
    </div>
  );
}

export default ReducerSample;

출처

  • 패스트캠퍼스 for velopert

Leave a comment