let's get IT with DAVINA ๐ป
๊ธฐ์กด React์ TypeScript ์ ์ฉํ๊ธฐ ๋ณธ๋ฌธ
1. TypeScript ์ ์ฉ
(1) ์๋ก์ด ํ๋ก์ ํธ๋ฅผ TypeScript๋ก ์์ฑํ๊ธฐ
npx create-react-app ํ๋ก์ ํธ์ด๋ฆ --template typescript
(2) ๊ธฐ์กด React์ TypeScript ์ ์ฉํ๊ธฐ
#npm
npm install typescript @types/node @types/react @types/react-dom @types/jest
#yarn
yarn add typescript @types/node @types/react @types/react-dom @types/jest
2. TypeScript ์ค์
tsc ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ tsconfig.json์ ์์ฑํ๋ค.
tsc --init
์ํ๋ ์ต์ ์ ์ถ๊ฐํ์ฌ tsconfig.json ์ ์ค์ ํ๋ค.
{
"compilerOptions": {
"outDir": "build",
"target": "es6", //์ด๋ค๋ฒ์ ์ js๋ก ์ปดํ์ผํ ์ง ์ ์
"lib": [
//์ด๋ค ํ๊ฒฝ์์ ์ฌ์ฉํ๋์ง ์ ์(api์๋์์ฑ ์ ๊ณตํด์ค)
"dom", //๋ธ๋ผ์ฐ์
"dom.iterable",
"esnext"
],
"baseUrl": "./src", // ์ถ๊ฐ๋ ๊ฒ ./src๋ฅผ ์ ๋ ๊ฒฝ๋ก๋ก ์ง์
"allowJs": true, //ts์์์ jsํ์ฉ(jsํ์ผ import๊ฐ๋ฅ)
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
//์ปดํ์ผํ ํด๋ ์
๋ ฅ. src์ ๋ชจ๋ ํ์ผ ํ์ธํจ
"src"
],
"exclude": ["node_modules"]
}
https://www.typescriptlang.org/tsconfig
3. ์คํฌ๋ฆฝํธ ์์
.js / .jsx ํ์ผ๋ค์ .ts / .tsx๋ก ๋ณ๊ฒฝํ๋ค.
index.js → index.tsx
- โ๏ธ as HTMLElement์ถ๊ฐ
- getElementById๋ก ๋ฐ๋ ๊ฐ์ฒด ํ์ ์ ์ง์ ํด์ TS๊ฐ ๋ฐ์ดํฐ ํ์ ์ ์ ์ ์๊ฒ ํด์ค
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement //โ๏ธ as HTMLElement์ถ๊ฐ
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
App.js → App.tsx
useState<Generics>()
state์ type์ ์ง์ ํ ๋์๋ ์์ ๊ฐ์ด Generics์์ ํ์ ์ ์ง์ ํด์ฃผ๋ฉด ๋๋ค. ๊ทธ๋ฐ๋ฐ ์ฌ์ค ์ด๊ธฐ๊ฐ์ ์ง์ ํด์ฃผ๋ฉด ์์์ ํ์ ์ ์ ์ถํ๊ธฐ ๋๋ฌธ์ ๊ตณ์ด ์ง์ ํด์ฃผ์ง ์์๋ ๋ฌด๋ฐฉํ๋ค.
๋ค๋ง, ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ์๋ Generics์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
โถ ์ํ๊ฐ null์ผ ์๋ ์๊ณ ์๋์๋ ์์๋
โถ ์ํ์ ํ์ ์ด ๊น๋ค๋ก์ด ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง ๊ฐ์ฒด์ด๊ฑฐ๋ ๋ฐฐ์ด์ผ ๋
https://velog.io/@jjburi/TypeScript-useState%EC%97%90%EC%84%9C-type-%EC%A7%80%EC%A0%95
import React, { useState } from "react";
import { MdAddCircle } from "react-icons/md";
import "./App.css";
import Template from "./components/Template";
import TodoInsert from "./components/TodoInsert";
import TodoList from "./components/TodoList";
export interface Todo {
id: number;
text: string;
checked: boolean;
}
let nextId = 4;
const App = () => {
const [selectedTodo, setSelectedTodo] = useState<Todo | null>(null);
const [insertToggle, setInsertToggle] = useState<boolean>(false);
const [todos, setTodos] = useState<Todo[]>([
{
id: 1,
text: "ํ ์ผ 1",
checked: true,
},
{
id: 2,
text: "ํ ์ผ 2",
checked: false,
},
{
id: 3,
text: "ํ ์ผ 3",
checked: true,
},
]);
const onInsertToggle = () => {
if (selectedTodo) {
setSelectedTodo(null);
}
setInsertToggle((prev) => !prev);
};
const onInsertTodo = (text: string) => {
if (text === "") {
return alert("ํ ์ผ์ ์
๋ ฅํด์ฃผ์ธ์.");
} else {
const todo = {
id: nextId,
text,
checked: false,
};
setTodos((todos) => todos.concat(todo)); //์ํ ๋ถ๋ณ์ฑ ์ ์ง push ๋์ concat
nextId++;
}
};
const onCheckToggle = (id: number) => {
setTodos((todos) =>
todos.map((todo) =>
todo.id === id ? { ...todo, checked: !todo.checked } : todo
)
);
};
const onChangeSelectedTodo = (todo: Todo | null) => {
setSelectedTodo(todo);
};
const onRemove = (id: number) => {
onInsertToggle();
setTodos((todos) => todos.filter((todo) => todo.id !== id));
};
const onUpdate = (id: number, text: string) => {
onInsertToggle();
setTodos((todos) =>
todos.map((todo) => (todo.id === id ? { ...todo, text } : todo))
);
};
return (
<Template todoLength={todos.length}>
<TodoList
todos={todos}
onCheckToggle={onCheckToggle}
onInsertToggle={onInsertToggle}
onChangeSelectedTodo={onChangeSelectedTodo}
/>
<div className='add-todo-button' onClick={onInsertToggle}>
<MdAddCircle />
</div>
{insertToggle && (
<TodoInsert
selectedTodo={selectedTodo}
onInsertToggle={onInsertToggle}
onInsertTodo={onInsertTodo}
onRemove={onRemove}
onUpdate={onUpdate}
/>
)}
</Template>
);
};
export default App;
๋ค๋ฅธ component๋ค์์ props type ์ง์ ํด์ฃผ๊ธฐ
ํจ์๊ฐ props๋ก ์ ๋ฌ๋ ๋, ๋ฑํ return์ ํ๊ณ ์์ง ์๋ค๋ฉด void๋ก ์ค์ !
interface PropsType {
todo: Todo;
onCheckToggle: (id: number) => void;
onInsertToggle: () => void;
onChangeSelectedTodo: (todo: Todo | null) => void;
}
const TodoItem = ({
todo,
onCheckToggle,
onInsertToggle,
onChangeSelectedTodo,
}: PropsType) => {
const { id, text, checked } = todo;
return (
...์๋ต
event type ์ค์ ํด์ฃผ๊ธฐ
๐ซ event ๋ target์ ๊ฐ์ง๊ณ ์์ง์๋ค
.onChange์ React.ChangeEventHandler<HTMLInputElement>๋ฅผ ์ ์ฉํด์ฃผ๋ ๋จ๋ ์๋ฌ..
solve
ChangeEventHandler -> ChangeEvent
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
return (
<input
placeholder='add your to-do'
value={value}
onChange={onChange}
></input>
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onInsertTodo(value);
setValue("");
onInsertToggle();
};
return (
<form
onSubmit={
selectedTodo? () => {
onUpdate(selectedTodo.id, value);
}: onSubmit}
>
</form>
'DEV_IN > TypeScript' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
โ๏ธ ์์ฃผ ๋ณผ ์ ์๋ Errors (0) | 2023.02.17 |
---|---|
TypeScript(apply) (2) | 2023.02.17 |
TypeScript(Interface) (4) | 2023.02.16 |
TypeScript(Classes) (4) | 2023.02.12 |
TypeScript (Function) (3) | 2023.02.09 |