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 확장자 사용
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>
);
};
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>
);
};
변경사항
- Switch Component -> Routes Component
- Route Component Props: component -> element ( element에 component로 전달)
- Route Component Props: exact 선언 x
[참고사이트]
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);
};
});
📎 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지정 */}
[참고 사이트]
https://driip.me/7126d5d5-1937-44a8-98ed-f9065a7c35b5
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);
'Archive' 카테고리의 다른 글
[TS] keyof / Mapped Types / infer 등 (0) | 2022.01.17 |
---|---|
[TS] declare / d.ts / index signatures (0) | 2022.01.13 |
[JS] 비동기 / Promise / async.await (0) | 2022.01.06 |
[GIT] Github 잔디 안심어지는 현상 (0) | 2022.01.05 |
[JS] getter,setter / import,export (0) | 2022.01.05 |