타입스크립트 리액트 시작하기
npm i @types/node @types/react @types/react-dom @types/jest
// tsconfig.json
{
"compilerOptions": {
"target": "ES5",
"module": "CommonJS",
"strict": true,
"allowJS": true,
"esModuleInterop": true,
"jsx": "react-jsx"
},
"include": ["src"]
}
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
function App() {
return <div />;
}
export default App;
상태관리와 Props
import React, { useReducer, useRef } from "react";
import Editor from "@/components/Editor";
import TodoItem from "@/components/TodoItem";
import { Todo } from "types.ts";
type Action = {
type: "CREATE";
payload: Todo;
} | {
type: "DELETE";
payload: number;
};
const reducer = (state: Todo[], action: Action) => {
switch (action.type) {
case "CREATE":
return [...state, action.payload];
case "DELETE":
return state.filter((todo) => todo.id !== action.payload);
default:
return state;
}
};
export const TodoStateContext = React.createContext<Todo[]>([]);
export const TodoDispatchContext = React.createContext<
React.Dispatch<Action>
| undefined
>(undefined);
function App() {
const [todos, dispatch] = useReducer(reducer, []);
const idRef = useRef(0);
return (
<TodoStateContext.Provider value={todos}>
<TodoDispatchContext.Provider value={dispatch}>
<h1>Todo</h1>
<Editor idRef={idRef} />
<div>
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} />
))}
</div>
</TodoDispatchContext.Provider>
</TodoStateContext.Provider>
);
}
export default App;
import React, { useState, useContext, useRef } from "react";
import { TodoDispatchContext } from "../App";
interface Props {
idRef: React.MutableRefObject<number>;
}
const Editor: React.FC<Props> = ({ idRef }) => {
const [text, setText] = useState("");
const dispatch = useContext(TodoDispatchContext);
const onChangeInput = (e: React.ChangeEvent<HTMLInputElement>) => {
setText(e.target.value);
};
const onClickButton = () => {
if (dispatch) {
dispatch({ type: "CREATE", payload: { id: idRef.current++, content: text } });
setText("");
}
};
return (
<div>
<input value={text} onChange={onChangeInput} />
<button onClick={onClickButton}>Add</button>
</div>
);
};
export default Editor;
import React, { useContext } from "react";
import { TodoDispatchContext } from "../App";
import { Todo } from "types.ts";
interface Props {
todo: Todo;
}
const TodoItem: React.FC<Props> = ({ todo }) => {
const dispatch = useContext(TodoDispatchContext);
const onClickDelete = () => {
if (dispatch) {
dispatch({ type: "DELETE", payload: todo.id });
}
};
return (
<div>
{todo.id}: {todo.content}
<button onClick={onClickDelete}>Delete</button>
</div>
);
};
export default TodoItem;
export interface Todo {
id: number;
content: string;
}
Context API
import React, { useContext } from "react";
export const TodoStateContext = React.createContext<Todo[] | null>(null)
export const TodoDispatcchContext = React.createContext<{
onClickAdd: (text: string) => void;
onClickDelete: (id: number) => void;
} | null>(null);
export const useTodoDispatch = () => {
const dispatch = useContext(TodoDispatchContext);
if (!dispatch) throw new Error("err");
return dispatch;
}
<TodoStateContext.Provider value={todos}>
<TodoDispatchContext.Provider
value={{
onClickAdd,
onClickDelete,
}}
>
<Editor />
<div>
{todos.map(todo) => (
<TodoItem key={todo.id} {...todo} />
))}
</div
</TodoDispatchContext.Provider>}>
</TodoStateContext.Provider>
Reference