Stack/TypeScript

[TS / React] Redux로 Counter 구현

7ingout 2022. 7. 28. 12:09

react-redux

1. 모듈리듀서

1) 액션타입 지정

2) 액션생성 함수

3) 초기값

4) 리듀서 함수 작성

-> 루트리듀서 

 

2. 스토어 생성

const store = createStore(루트리듀서)

<Provider store={store}>

                <App/>

</Provider>

 

3.컴포넌트 생성

1) 컨테이너 컴포넌트

2) 프레젠테이션 컴포넌트

 


 

npx create-react-app ts-redux-tutorial --template typescript

ts-redux-tutorial

npm install redux
npm install react-redux

ts 안붙어있는 react-redux는 타입스크립트를 모듈에 적용시켜야 함

npm install @types/react-redux

 

 

* 카운터 만들기

1. 모듈리듀서 -> 루트리듀서 -> combine

2. 컴포넌트

 

modules/counter.ts

as const를 붙여줘야 함

// 1. 액션타입
// 2. 액션을 리턴해주는 함수
// 3. 초기값
// 4. 리듀서 만들기

// 액션타입
export const INCREASE = "counter/INCREASE" as const;
export const DECREASE = "counter/DECREASE" as const;
export const INCREASE_BY = "counter/INCREASE_BY" as const;

// 액션 생성함수
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 = {
    count: 0
}

// 리듀서 액션 타입지정
// ReturnType<typeof ---> 특정함수의 반환값을 추론

// 리듀서 액션 타입지정
type CounterAction = 
| ReturnType<typeof increase>
| ReturnType<typeof decrease>
| ReturnType<typeof increaseBy>

// 리듀서 만들기
export default function counter(state: CounterState = initialState, action: CounterAction) : CounterState{
    switch(action.type) {
        case 'counter/INCREASE':
            return { count: state.count + 1 };
        case 'counter/DECREASE':
            return { count: state.count - 1 };
        case 'counter/INCREASE_BY':
            return { count: state.count + action.payload };
        default:
            return state;
    }
}

 

modules/index.ts

import { combineReducers } from "redux";
import counter from "./counter"

const rootReducer = combineReducers({ counter });
export default rootReducer;

// 나중에 이 타입을 컨테이너 컴포넌트에서 불러와서 사용해야하므로
// 내보내줍니다.
export type RootState = ReturnType<typeof rootReducer>

 

App.tsx

import React from 'react';
import './App.css';
import ContainerCounter from './containers/ContainerCounter';

function App() {
  return (
    <div className="App">
        <ContainerCounter />
    </div>
  );
}

export default App;

 

index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { createStore } from 'redux';
import rootReducer from './modules';
import { Provider } from 'react-redux';

const store = createStore(rootReducer)

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

containers/CountainerCounter.tsx

import React from 'react';
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import Counter from '../components/Counter';
import { RootState } from '../modules';
import { decrease, increase, increaseBy } from '../modules/counter'

const ContainerCounter = () => {
    const count = useSelector((state:RootState)=>state.counter.count);
    const dispatch = useDispatch();

    const onIncrease = () => {
        dispatch(increase())
    }
    const onDecrease = () => {
        dispatch(decrease());
    }
    const onIncreaseBy = (diff:number) => {
        dispatch(increaseBy(diff));
    }
    return (
        <Counter count={count}
        onDecrease={onDecrease}
        onIncrease={onIncrease}
        onIncreaseBy={onIncreaseBy}/>
    );
};

export default ContainerCounter;

 

components/Counter.tsx

import React from 'react';

type CounterProps = {
    count: number;
    onIncrease: () => void;
    onDecrease: () => void;
    onIncreaseBy: (diff:number) => void;
}

const 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;