typescript를 복습하기 위해 기본적인 예제를 만들어보는 시간
next.js
,redux
,Typescript
를 사용하였습니다.
예제 생성은npx create-next-app --example with-typescript with-typescript-app
사용했습니다.
├── components
│ └── Counter.tsx
├── containers
│ └── CounterContainer.tsx
├── hooks
│ └── useCounter.tsx
├── modules
│ ├── counter.ts
│ └── index.ts
├── pages
│ ├── _app.tsx
│ ├── api
│ └── index.tsx
├── public
│ ├── favicon.ico
│ └── vercel.svg
├── styles
│ ├── Home.module.css
│ └── globals.css
└── tsconfig.json
// action type
const INCREASE = <const>'counter/INCREASE'
const DECREASE = <const>'counter/DECREASE'
const INCREASE_BY = <const>'counter/INCREASE_BY'
<const>
의 뜻은 타입 추론을 할 수 있도록 하기 위한 것이다. 만약 <const>
제너릭을 사용하지 않고 그대로 놔뒀을 경우 type을 string
으로 인식하게 된다.
// action function
export const increase = () => ({type: INCREASE})
export const decrease = () => ({type: DECREASE})
export const increaseBy = (diff: number) => ({type: INCREASE_BY, payload: diff})
//initialStateType
interface initialStateType {
count: number
}
//initialState
const intialState = {count: 0}
function counter(state: initialStateType = intialState, action: CounterAction) {
switch (action.type) {
case INCREASE:
return {state: state.count + 1}
case DECREASE:
return {state: state.count - 1}
case INCREASE_BY:
return {state: state.count + action.payload}
default:
return state;
}
}
export default counter
import {combineReducers} from "redux";
import counter from './counter';
export type RootState = ReturnType<typeof rootReducer>;
const rootReducer = combineReducers({counter})
export default rootReducer;
import '../styles/globals.css'
import type {AppProps} from 'next/app'
import {Provider} from "react-redux";
import {createStore} from "redux";
import rootReducer from "../modules";
const store = createStore(rootReducer)
function MyApp({Component, pageProps}: AppProps) {
return (
<>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</>
)
}
export default MyApp
import React from 'react';
interface TypeProps {
count: number
increase: () => void
decrease: () => void
increaseBy: (diff: number) => void
}
const Counter = ({count, increase, decrease, increaseBy}:TypeProps) => {
return (
<>
<h1>{count}</h1>
<button onClick={increase}>+</button>
<button onClick={decrease}>-</button>
<button onClick={() => increaseBy(5)}>increaseBy</button>
</>
);
};
export default Counter;
props로 전달받은 인자들만을 이용해 순수하게 UI 로직만 있는 코드를 작성합니다.
import React from 'react';
import Counter from "../components/Counter";
import {RootState} from "../modules";
import {useDispatch, useSelector} from "react-redux";
import {decrease, increase, increaseBy} from "../modules/counter";
const CounterContainer = () => {
const count = useSelector((state: RootState) => state.counter.count)
const dispatch = useDispatch();
const onIncrease = () => {
dispatch(increase());
}
const onDecrease = () => {
dispatch(decrease())
}
const onIncreaseBy = () => {
dispatch(increaseBy(5))
}
return (
<Counter count={count} increase={onIncrease} decrease={onDecrease} increaseBy={onIncreaseBy} />
);
};
export default CounterContainer;
프리젠테이션 컴포넌트와 컨테이너 컴포넌트를 분리하지 않고 커스텀 훅을 만들어 한 번에 처리할 수도 있다.
프리젠테이션, 컨테이너 컴포넌트 차이점은 링크에 잘 나와있다!
import React from 'react';
import {useDispatch, useSelector} from "react-redux";
import {RootState} from "../modules";
import {decrease, increase, increaseBy} from "../modules/counter";
const UseCounter = () => {
const dispatch = useDispatch();
const count = useSelector((state: RootState) => state.counter.count);
const onIncrease = () => {
dispatch(increase())
}
const onDecrease = () => {
dispatch(decrease())
}
const onIncreaseBy = (diff:number) => {
dispatch(increaseBy(diff))
}
return {
count, onIncrease, onDecrease, onIncreaseBy
}
};
export default UseCounter;
기존과 같이 props로 전달해준 것이 아닌 hook을 만들어 해당하는 값들을 return 받아 Counter
컴포넌트에서 바로 사용할 수 있다.
import React from 'react';
import useCounter from '../hooks/useCounter'
const Counter = () => {
const {count, onIncrease, onDecrease, onIncreaseBy} = useCounter();
return (
<>
<h1>{count}</h1>
<button onClick={onIncrease}>+</button>
<button onClick={onDecrease}>-</button>
<button onClick={() => onIncreaseBy(5)}>increaseBy</button>
</>
);
};
export default Counter