React + TS 03 | 타입스크립트로 리액트 상태 관리

Kate Jung·2021년 12월 27일
0

React

목록 보기
19/28
post-thumbnail
post-custom-banner

📌 섹션 목표

TS를 사용하는 리액트 컴포넌트에서 컴포넌트의 상태를 관리(useState & useReducer 사용)하는 방법 학습

📌 카운터 제작

🔹 src/Counter.tsx

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState<number>(0);
  const onIncrease = () => setCount(count + 1);
  const onDecrease = () => setCount(count - 1);
  return (
    <div>
      <h1>{count}</h1>
      <div>
        <button onClick={onIncrease}>+1</button>
        <button onClick={onDecrease}>-1</button>
      </div>
    </div>
  );
}

export default Counter;
  • 리액트 컴포넌트와의 차이

    Generics 사용 → 상태의 타입 설정 (useState<number>())

    • 안해도 무방

      (Generics 없어도 ) 알아서 타입 유추 → 생략해도 상관 x


    • Generics 사용하는 것이 좋은 경우 (useState 사용 시)

      상태가 null일 수도 있고 아닐수도 있을때

        // 예시
        type Infomation = {
            name: string;
            description: string;
          };
        
        const [info, setInfo] = useState<Infomation | null>(null);

🔹 렌더링

// src/App.tsx
import React from 'react';
import Counter from './Counter';

const App: React.FC = () => {
  return <Counter />;
};

export default App;

📌 인풋 상태 관리

🔹 컴포넌트 작성

// 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) => {
    // e의 타입을 모를 때: 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;

📌 useReducer 사용

🔹 예시 코드 1

// src/Counter.tsx

import React, { useReducer } from 'react';

type Action = { type: 'INCREASE' } | { type: 'DECREASE' }; // 이렇게 액션을 | 으로 연달아서 쭉 나열하세요.

function reducer(state: number, action: Action): number {
  switch (action.type) {
    case 'INCREASE':
      return state + 1;
    case 'DECREASE':
      return state - 1;
    default:
      throw new Error('Unhandled action');
  }
}

function Counter() {
  const [count, dispatch] = useReducer(reducer, 0);
  const onIncrease = () => dispatch({ type: 'INCREASE' });
  const onDecrease = () => dispatch({ type: 'DECREASE' });

  return (
    <div>
      <h1>{count}</h1>
      <div>
        <button onClick={onIncrease}>+1</button>
        <button onClick={onDecrease}>-1</button>
      </div>
    </div>
  );
}

export default Counter;

🔸 액션 객체에 type 외 다른 값들이 있는 경우

다른 값들도 타입 안에 명시 한다면

→ 리듀서 작성 시 자동완성 됨.

dispatch 할 때 타입검사 함.

🔹 예시 코드 2

// src/ReducerSample.tsx

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_ISGOOD" };

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_ISGOOD":
      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_ISGOOD" });

  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_ISGOOD</button>
      </div>
    </div>
  );
}

export default ReducerSample;

🔹 렌더링

// App.tsx
import React from 'react';
import ReducerSample from './ReducerSample';

const App: React.FC = () => {
  return <ReducerSample />;
};

export default App;

참고

profile
복습 목적 블로그 입니다.
post-custom-banner

0개의 댓글