본문 바로가기
coding/front-end

[React]Redux Toolkit (리덕스 툴킷)사용해 보기- Typescript

by 꾸준한 개발 2022. 5. 10.
반응형

import React from 'react';
import './App.css';
import Btn from './component/Btn';
import Input from './component/Input';
import { useAppSelector } from './store';

function App() {
  const { value } = useAppSelector((state) => state.input);
  

  return (
    <div className='container'>
      <div className='value'>{value}</div>
      <Input value={value}/>
      <Btn />
    </div>
  );
}

export default App;

React에서 상태 관리할 때 가장 많이 사용하는 툴이 Redux인데요. Redux를 사용하기 전에 초기 코드를 설정하는 과정이 정말 길고 복잡합니다. 그리고 중간에 store에 새로운 데이터를 만드는 일이 생기면 reducer파일에서 다시 만들고 action 파일에서 만들고 해야 하는 번거로움이 있는데요. Redux Toolkit을 사용해서 좀 더 간편하게 redux을 사용할 수 있게 되었습니다.

 

https://redux-toolkit.js.org/

 

Redux Toolkit | Redux Toolkit

The official, opinionated, batteries-included toolset for efficient Redux development

redux-toolkit.js.org

 

파일 구조


 

/src
├── /components
│	├── Input.tsx
│	└── Btn.tsx
│
├── index.tsx
├── App.tsx
├── App.css
└── /store
    ├── slices
    │	├── counterSlice.ts
    │	└── inputSlice.ts
    │
    └── index.ts // store

 

Store/index.ts


import { configureStore, combineReducers } from "@reduxjs/toolkit";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { createLogger } from 'redux-logger';

import countSlice from './slices/counterSlice';
import inputSlice from "./slices/inputSlice";

// 리덕스 미들웨어
const logger = createLogger();

// 모든 reducers
const rootReducer = combineReducers({
    
    counter: countSlice.reducer,
    input: inputSlice.reducer,
});

const initialState = {};

// 저장소 만들기
export const store = configureStore({
    reducer: rootReducer,
    middleware: (middleware) => middleware().concat(logger),
    devTools: process.env.NODE_ENV !== 'production',
    preloadedState: initialState,
    enhancers: (defaultEnhancers) => [...defaultEnhancers],
})

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useAppDispatch = () => useDispatch<AppDispatch>();

export default store;

store/index.ts 에서는 store을 생성하고 slices 파일에서 생성한 reducer들을 가지고 rootreducer을 생성해서 store생성할 때 넣어줍니다.

 

configureStore

- reducer: rootreducer을 넣어줍니다. 

- middleware: 콜백 함수을 넣어줍니다. parameter에는 기본에 설정되어 있는 미들웨어들이고 추가적으로 사용할 미들웨어를 합춰줍니다. 작성하지 않으면 기본(getDefaultMiddleware) 미들웨어만 담깁니다.

- devTools: boolean 타입으로 on off 할 수 있습니다.

- preloadState: store의 초기값 세팅입니다.

- enhancers: 배열 또는 콜백함수를 받습니다. 개발자가 미들웨어의 순서를 지정할 수 있습니다.

 

useAppSelector, useAppDispatch

- useSelector와 useDispatch들을 타입을 지정해 줘서 ts에서 사용할 수 있게 했습니다.

 

 

Slices/input.tsx


import { createSlice, PayloadAction } from "@reduxjs/toolkit";

interface CommonState {
    value: string
}

const initialState: CommonState = {
    value: ''
}

const inputSlice = createSlice({
    name: 'input',
    initialState,
    reducers: { // reducer
        setInputValue: {
            reducer: (state, action: PayloadAction<string>) => {
                state.value = '';
            },
            prepare: (text: string) => ({
                payload: text,
            }),
        },
        resetInputValue(state, action: PayloadAction) {
            state.value = '';
        }
    }
})


// counter action function
export const { setInputValue, resetInputValue } = inputSlice.actions;

export default inputSlice.reducer;

createSlice

 - name: 각각의 이름을 적어줍니다.

 - initialState: 초기 state값을 정해줍니다. 

 - redcuers: reducers안에 함수들이 action함수가 되고 action type은 name/reducers의 함수명 이됩니다.

   - resetInputValue : payload에 따로 값이 없을 때 해주기 좋다.

   - setInputValue payload가 따로 존재할 때 prepare을 사용해서 action에서 받은 arguments 들을 playload에 넣어준다.

 

App.tsx


import React from 'react';
import './App.css';
import Btn from './component/Btn';
import Input from './component/Input';
import { useAppSelector } from './store';

function App() {
  const { value } = useAppSelector((state) => state.input);
  

  return (
    <div className='container'>
      <div className='value'>{value}</div>
      <Input value={value}/>
      <Btn />
    </div>
  );
}

export default App;

 

useAppSelector을 이용해서 input reducer에 value 값을 얻었습니다.

 

index.tsx


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

import store from './store/index';

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

// 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();

최상위 폴더에 provider을 이용해서 store을 넣어줬습니다. 그러므로 하위 컴포넌트들이 store에 다 접근할 수 있게 해 주었습니다.

 

 

Input.tsx


import { useAppDispatch } from "../store";
import { setInputValue } from '../store/slices/inputSlice';

export default function Input({ value }: { value: string }) {
    const dispatch = useAppDispatch();
    const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        dispatch(setInputValue(e.target.value));
    }

    return (
        <input name='test' value={value} onChange={onChange}/>
    )
}

 

store.index 에서 만든 useAppDispatch을 이용해서 setInputValue action에 payload를 담아서 reducer에게 보내주었습니다.

 

 

Btn.tsx


import { useAppDispatch } from "../store";
import { resetInputValue } from '../store/slices/inputSlice';

export default function Btn() {
    const dispatch = useAppDispatch();
    
    const onClick = () => {
        dispatch(resetInputValue());
    }
    return (
        <button onClick={onClick}>리셋</button>
    )
}

리셋할 때에는 따로 payload를 지정해줄 않았기 때문에 인자에 아무것도 넣어주지 않았습니다.

 

반응형

댓글