npx create-react-app [프로젝트명] --template typescript
npm i redux react-redux
npm i @types/react-redux
: react-redux는 타입스크립트가 적용 안되어있기 때문에 이렇게 @types에서 설치해준다.
count 앱 만들기
modules/counter.ts
//액션 타입 정의
const INCREASE = 'counter/INCREASE' as const;
const DECREASE = 'counter/DECREASE' as const;
const INCREASE_BY = 'counter/INCREASE_BY' as const;
// as const를 붙이기 전까지는 타입을 예측할 수 없다.
// 붙이고 나면 정상적으로 타입이 counter/INCREASE로 나오는걸 볼 수 있다.
//액션 생성함수
export const increase =() => ({ type: INCREASE });
export const decrease =() => ({ type: DECREASE });
export const increaseBy = (diff:number) => ({
type: INCREASE_BY,
payload: diff
})
type CounterState = {
count : number;
}
const initialState:CounterState = {
count:0
}
// 이렇게 위에 선언한 함수의 결과물을 가져올 수 있다.
type CounterAction =
|ReturnType<typeof increase>
|ReturnType<typeof decrease>
|ReturnType<typeof increaseBy>
//리듀서 함수 시작
function counter(state: CounterState = initialState, action:CounterAction):CounterState{
switch (action.type){
case INCREASE:
return {count:state.count +1}
case DECREASE:
return {count:state.count -1}
case INCREASE_BY:
return {count:state.count + action.payload}
default:
return state;
}
}
export default counter;
modules/index.ts
import {combineReducers} from 'redux';
import counter from './counter';
//루트리듀서 만들기
const rootReducer = combineReducers({
counter
})
export default rootReducer;
export type RootState = ReturnType<typeof rootReducer>;
index.tsx
import React from 'react';
import ReactDom from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {Provider} from 'react-redux';
import {createStore} from 'redux'
import rootReducer from './modules';
const store = createStore(rootReducer);
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>.
document.getElementById('root')
);
components/Counter.tsx
import React from 'react';
type CounterProps = {
count: number;
onIncrease: () =>void;
onDecrease: () =>void;
onIncreaseBy: (diff:number)=> void;
};
function Counter({
count,
onIncrease,
onDecrease,
onIncreaseBy
}:CounterProps) {
return (
<div>
<h1>{count}</h1>
<button onClick = {onIncrease}>+1</button>
<button onClick = {onDecrease}>-1</button>
<button onClick = {()=> onIncreaseBy(5)}>+5</button>
</div>
)
}
export default Counter;
container/CounterContainer.tsx
import React from 'react';
import Counter from '../components/Counter';
import {useSelector} from 'react-redux';
import {RootState} from '../modules';
function CounterContainer() {
const count = useSelector((state:RootState) => state.counter.count);
const dispatch = useDispatch();
const onIncrease = () => {
dispatch(increase());
}
const onDecrease = () =>{
dispatch(decrease());
}
const onIncreaseBy = (diff:number) => {
dispatch(increase(diff));
}
return (
<Counter
count={count}
onIncrease={onIncrease}
onDecrease={onDecrease}
onIncreaseBy={onIncreaseBy}
/>
)
}
export default CounterContainer
App.tsx
import React from 'react';
import CountContainer from './containers/CounterContainer';
const App:React.FC = () => {
return <CounterContainer />
}
export default App;