191101 TIL TypeScript
Nov 1, 2019
»
1.5막,
TIL (Today I Learned)
오늘 할 일
- TS 배우기
- MobX && TS && React 프로젝트 구현해보기
Greetings.tsx 만들기
npx create-react-app {프로젝트명} --typescript
// Greetings.tsx
import React from 'react';
type GreetingsProps = {
name: string;
mark: string;
optional?: string;
onClick: (name: string) => void
};
const Greetings = ({name, mark, optional, onClick}: GreetingsProps) => {
const handleClick = () => onClick(name);
return (
<div>
Hello, {name} {mark}
{optional && <p> {optional} </p>}
<div>
<button onClick={handleClick}>
click me
</button>
</div>
</div>
);
};
export default Greetings;
// App.tsx
import React from 'react';
import Greetings from "./components/Greetings";
const App: React.FC = () => {
const onClick = (name: string) => console.log(name);
return (
<Greetings name={'soom'} mark={'!'} onClick={onClick}/>
);
}
export default App;
greetings에서 왜 handleClick
을 선언해줘야하나, 하고 그냥 내 맘대로 아래처럼 했는데,
<button onClick={onClick}>
click me
</button>
에러남
Type '(name: string) => void' is not assignable to type '(event: MouseEvent<HTMLButtonElement, MouseEvent>) => void'.
Types of parameters 'name' and 'event' are incompatible.
Type 'MouseEvent<HTMLButtonElement, MouseEvent>' is not assignable to type 'string'.
e 인자를 받아오는데, 그게 string이 아니라는 뜻 같음. 그래서 아래처럼 해주면 된다.
<button onClick={(e) => onClick(name)}>
click me
</button>
MyForm.tsx 만들기
// MyForm.tsx
import React, { useState } from 'react';
type FormType = {
name: string;
description: string
}
type MyFormProps = {
onSubmit: (form: FormType) => void;
}
export default function MyForm({ onSubmit }: MyFormProps) {
const [form, setForm] = useState({ name: '', description: '' });
const { name, description } = form;
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setForm({ ...form, [name]: value })
}
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit(form);
setForm({ name: '', description: '' })
}
return (
<form onSubmit={handleSubmit}>
<input name="name" value={name} onChange={onChange}/>
<input name="description" value={description} onChange={onChange}/>
<button type="submit">등록</button>
</form>
)
}
// App.tsx
const App: React.FC = () => {
const onSubmit = (form: { name: string; description: string}) => {
console.log(form);
}
return <MyForm onSubmit={onSubmit}/>
}
Counter.tsx 만들기
// 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 type...')
}
}
export default 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>
)
}
###Context 만들기
// SampleContext.tsx
import React, { createContext, Dispatch, useContext, useReducer } from "react";
type Color = 'red' | 'yellow' | 'black';
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_GOOD' }
function reducer(state: State, action: Action): State {
switch (action.type) {
case "SET_COUNT":
return { ...state, count: action.count };
case "SET_TEXT":
return { ...state, text: action.text };
case "SET_COLOR":
return { ...state, color: action.color };
case "TOGGLE_GOOD":
return { ...state, isGood: !state.isGood };
default:
throw new Error('에 러 발 생!')
}
}
const SampleStateContext = createContext<State | null>(null);
const SampleDispatchContext = createContext<Dispatch<Action> | null>(null);
type SampleProviderProps = {
children: React.ReactNode;
}
export function SampleProvider({children} : SampleProviderProps) {
const [state, dispatch] = useReducer(reducer, {
count: 0,
text: 'hihihihi',
color: 'red',
isGood: true
});
return (
<SampleStateContext.Provider value={state}>
<SampleDispatchContext.Provider value={dispatch}>
{children}
</SampleDispatchContext.Provider>
</SampleStateContext.Provider>
)
}
export function useSampleState() {
const state = useContext(SampleStateContext);
if (!state) throw new Error('error')
return state;
}
export function useSampleDispatch() {
const dispatch = useContext(SampleDispatchContext);
if (!dispatch) throw new Error('errorrrrr');
return dispatch;
}
//ReduceSample.tsx
import React from 'react';
import { useSampleDispatch, useSampleState } from "../SampleContext";
export default function ReducerSample() {
const state = useSampleState();
const dispatch = useSampleDispatch();
const setCount = () => dispatch({ type: "SET_COUNT", count: 5 });
const setText = () => dispatch({ type: "SET_TEXT", text: '룰루' });
const setColor = () => dispatch({ type: "SET_COLOR", color: 'red' });
const toggleGood = () => dispatch({ type: "TOGGLE_GOOD" });
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 </button>
</div>
</div>
)
}
//App.tsx
<SampleProvider>
<ReducerSample/>
</SampleProvider>
으으.. contextAPI 너무 어려워…