ARCHIVE/TypeScript

[TS] React

man_on 2022. 1. 12. 17:05
반응형

 

 

 

     

     

     

     

     

     

    React -  TypeScript 

     

     

     

    typescript 셋팅이 완료된 react 프로젝트 설치

    npx create-react-app 프로젝트명 --template typescript

     

     

    기존 프로젝트에 typescript만 더하고 싶으면

    npm install --save typescript @types/node @types/react @types/react-dom @types/jest

     

     

     🔻 JSX문법을 지원하는 tsx 확장자 사용

    .tsx

     

     

     

     

     


     

     

     

     

     

    JSX 타입지정

     

     

     

    🔻 JSX.Element

    let 박스 :JSX.Element = <div></div>
    let 버튼 :JSX.Element = <button></button>

     

    좀 더 정확하게 타입을 지정하고 싶으면 <div><a><h4>같은 기본 태그들은 JSX.IntrinsicElements

    let 박스 :JSX.IntrinsicElements['div'] = React.createElement('div');
    let 버튼 :JSX.IntrinsicElements['button'] = <button></button>;

     

     

    🔻state 타입지정

    자동으로 할당되어서 생략가능, 넣어야 될 상황에는 Generic 이용해서 useState에 넣어준다.

    const [user, setUser] = useState<string | null>('kim');

     

     

    📌 type assertion 문법

       : assertion 하고 싶으면 as 또는 <> 쓰면 되는데, 

        리엑트에서는 컴포넌트로 오해할 수 있어서 <>는 쓰지않고 as만 사용한다.

     

     

     

     

     

     


     

     

     

     

     

    Function / Props

     

     

     

    ✔️ 컴포넌트의 타입지정 ?

     

    함수니까 파라미터와 return 타입지정하면 된다.

    파라미터는 항상 props이므로 props가 어떻게 생겼는지 보고 타입지정하고

    return 타입은 JSX.Element를 써주면 된다. (생략해도 자동타입지정됨)

    type AppProps = {
      name: string;
    }; 
    
    function App (props: AppProps) :JSX.Element {
      return (
        <div>{message}</div>
      )
    }

     

     

     

     

    ✔️ React.FC

      arrow function 사용시 React.FC를 지정해주면 props에 자동의 children이 지정된다.

     하지만, children이 옵셔널 형태로 들어가 있으니, props의 타입이 명백하지않다는 단점이 생긴다.

     

    import React from "react";
    
    const Todos: React.FC = (props) => {
      return <ul>{props.children}</ul>;
    };
    
    export default Todos;

     

     

     

     

     

     

    📌  generic으로 props 지정 (props는 항상 객체이므로 <{ }> )

      중괄호 안에 props로 들어오는 형태를 그대로 type지정해주면된다.

    const Todos: React.FC<{ items: string[] }> = (props) => {
      return (
        <ul>
          {props.items.map((item) => (
            <li key={item}>{item}</li>
          ))}
        </ul>
      );
    };

    지정한 props 자동완성

     

     

     

     

    ex) 

    처음에 데이터는 배열안에 여러 객체이기 때문에 datatype[] 배열로 타입을 지정한다.

    export default function CarList() {
      const [data, setData] = useState<datatype[]>([]);
    
      //return (
        <div className="CardWrap">
          {data.map((datamap: datatype) => (
            <Card data={datamap} key={datamap.carClassId} />
          ))}
        </div>
      );
    }

     

    type폴더-types.ts에 따로 지정

    export interface datatype {
      carClassId: number;
      carClassName: string;
      carModel: string;
      image: string;
      drivingDistance: number;
      year: number;
      price: number;
      discountPercent: number;
      regionGroups: string[];
      carTypeTags: string[];
    }

    map으로 돌려서 props로 넘겨준 data는 하나의 객체들이기 때문에 type을 그냥 datatype으로 지정한다.

    import { datatype } from '../type/types';
    
    export default function Card({ data }: { data: datatype }) {
      return <div>{data.price}</div>;
    }

     

     

     

     

     

     

     


     

     

     

     

     

     

     

    Router

     

     

     

    import CardList from './components/CarList';
    import { BrowserRouter, Routes, Route } from 'react-router-dom';
    import './App.scss';
    
    const App = (): JSX.Element => {
      return (
        <BrowserRouter>
          <Routes>
            <Route path="/list" element={<CardList />} />
          </Routes>
        </BrowserRouter>
      );
    };
    
    export default App;

     

     

     

    - 오류발생

    const App = (): JSX.Element => {
      return (
          <BrowserRouter>
            <Switch>
              <Route path='/signin' component={Signin} />
              ...
            </Switch>
          </BrowserRouter>
      );
    };

     변경사항

    1. Switch Component -> Routes Component
    2. Route Component Props: component -> element ( element에 component로 전달)
    3. Route Component Props: exact 선언 x

     

     

     

    [참고사이트]

    https://msko.tistory.com/111

     

    React + TypeScript 기반 Router 설정하기

    React + TypeScript 기반 프로젝트 생성 npx create-react-app [Project name] --template typescript ※ 아래와 같은 오류 발생 시 You are running `create-react-app` 4.0.3, which is behind the latest rele..

    msko.tistory.com

     

     

     

     

     


     

     

     

     

     

     

     

     

    Event

     

     

    React.이번트지정<HTML요소지정>

     

     

    1. event지정

        form : React.FormEvent

        click : React.MouseEvent

        onChange : React.ChangeEvent

     

    const NewTodo = () => {
    
      const submitHandler = (event: React.FormEvent) => {
        event.preventDefault();
      };
      
      return (
        <form onSubmit={submitHandler}>
         ...
        </form>
      );

     

     

    2. Generic을 통해 HTML 노드 타입을 지정한다.

     

    예시) input태그의 onChange

      const editTitle = (e: React.ChangeEvent<HTMLInputElement>) => {
        const newList: any = replaceIndex(list, index, {
          ...item,
          title: e.target.value,
        });
        setList(newList);
      };

     

      const onUsernameChanege = (e: React.SyntheticEvent<HTMLInputElement>) => {
        const {
          currentTarget: { value },
        } = e;
        setUsername(value);
      };

     

     

     

     


     

     

     

     

     

     

     

    useRef

     

     

     

     

     useRef의 기능

       : useRef는 인자로 넘어온 초기값을 useRef 객체의 .current 프로퍼티에 저장한다.

    • DOM 객체를 직접 가리켜서 내부값을 변경
    • 변경되어도 component가 re-rendering되지 않도록 하기 위한 값을 저장

     

    //함수의 초기값을 .current에 저장한다.
    
    interface MutableRefObject<T> {
        current: T;
    }
    
    interface RefObject<T> {
        readonly current: T | null;
    }

     

     

     

    useRef의 3가지 정의

     

    1. useRef<T>(initialValue : T) : MutableRefObject<T>;

       : 로컬변수 용도로 사용하는 경우 제네릭 타입과 같은 타입의 초기값을 넣어준다. 

         (current 프로퍼티를 직접 수정 가능한 경우)

    const localVarRef = useRef<number>(0);

     

     

    2. useRef<T>(initialValue : T | null ) : RefObject<T>;

      : DOM을 직접 조작하기 위해 사용하는 경우 초기값으로 null을 넣어준다. 

        (current 프로퍼티를 직접 수정 불가능)

    const inputRef = useRef<HTMLInputElement>(null);

     

     

     

    3. useRef< T = undefiend >() : MutableRefObject<T | undefined >;

      : 제네릭 타입이 undefined인 경우 (타입 제공하지 않은 경우)

     

     

     

    📎 clickevent

      const modalRef = useRef() as React.MutableRefObject<HTMLDivElement>;
      
      const outsideClickHandler = (e: MouseEvent | React.BaseSyntheticEvent) => {
        if (!modalRef.current.contains(e.target)) {
          modal();
        }
      };
      
      useEffect(() => {
        document.addEventListener('mousedown', outsideClickHandler);
        return () => {
          document.removeEventListener('mousedown', outsideClickHandler);
        };
      });

    https://velog.io/@ptcookie78/TypeScript-React.js%EC%97%90%EC%84%9C-useRef-Hook-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

     

    TypeScript React.js에서 useRef Hook 사용하기

    useRef는 .current 프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환합니다. 반환된 객체는 컴포넌트의 전 생애주기를 통해 유지될 것입니다. 출처 - Hooks API

    velog.io

     

     

     

    📎 input에 ref

    더보기

    - InputRef = useRef < input의 타입속성 > ( 기본value값 )

    - InputRef.current?.value

       ? 지정 : string | null

       ! 지정 : string (null이 확실히 아닐 때 지정해줌)

     

    const NewTodo = () => {
      const todoTextInputRef = useRef<HTMLInputElement>(null);
      // <input의타입속성>(default value)
      const submitHandler = (event: React.FormEvent) => {
        event.preventDefault();
        const enteredText = todoTextInputRef.current?.value;
        // 물음표 IDE에서 자동생성 : ref가 아직 필수적으로 value를 설정하지않아서
        // enteredText는 undefined가 될수도 있고 string이 될수도있다(enteredText : string | undefined)
    
        // const enteredText = todoTextInputRef.current!.value;
        // 느낌표를 붙이면 current가 100% null일리 없다고 확신할 때 (enteredText : string)
        if (enteredText?.trim().length === 0) {
          //throw an error
          return;
        }
        
      };
      return (
        <form onSubmit={submitHandler}>
          <label htmlFor='text'>Todo text</label>
          <input type='text' id='text' ref={todoTextInputRef} />
          {/* ref지정 */}

     

    HTMLInputElement를 지정해줘서 모든입력요소 객체 프로퍼티가 자동생성

     

    검색 : 'mdn input'

     

     

     

     

    [참고 사이트]

    https://driip.me/7126d5d5-1937-44a8-98ed-f9065a7c35b5

     

    TypeScript React에서 useRef의 3가지 정의와 각각의 적절한 사용법

    Type 'MutableRefObject<... | undefined>' is not assignable to type ... 에러 좀 그만 보자!

    driip.me

     

     

     

     

     

     

     


     

     

     

     

     

    useState

     

     

     

    useState은 대부분 알아서 타입을 유추하기 때문에 생략해도 상관없다.

    - type을 지정하지않으면 never로 지정된다.

       

     

     

     

     

    Generics를 활용해야할 때

    • 상태의 타입이 까다로운 구조를 가진 객체이거나 배열일 때
    • default값은 빈배열이지만 나중에는 꽉찬 배열이 되므로 <Todo[]> 를 type으로 지정해준다.
    function App() {
      const [todos, setTodos] = useState<Todo[]>([]);
    
      const addTodoHandler = (todoText: string) => {
        const newTodo = new Todo(todoText);
    
        setTodos((prevTodos) => {
          return prevTodos.concat(newTodo);
        });
      };
    class Todo {
      id: string;
      text: string;
    
      constructor(todoText: string) {
        this.text = todoText;
        this.id = new Date().toISOString();
      }
    }
    
    export default Todo;

    Generics 사용안하고 싶을 때 - Type assertion

    type Todo = { id: number; text: string; done: boolean };
    const [todos, setTodos] = useState([] as Todo[]);

     

     

    • null일 수도 있고 아닐수도 있을때
    type Information = { name: string; description: string };
    const [info, setInformation] = useState<Information | null>(null);

     

     

     

     

     

    반응형