TS를 사용하는 리액트 컴포넌트에서 컴포넌트의 상태를 관리(
useState
&useReducer
사용)하는 방법 학습
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;
// 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;
다른 값들도 타입 안에 명시 한다면
→ 리듀서 작성 시 자동완성 됨.
→ dispatch
할 때 타입검사 함.
// 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;
참고