공부블로그

리덕스 미들웨어 redux-thunk 본문

리액트/리액트 공부

리덕스 미들웨어 redux-thunk

떠어영 2022. 9. 19. 17:41

에러땜에 짱 고생한 redux-thunk를 정리해보게따....

 

저번에 redux로 만든 todo-app redux에 middleware중 하나인 redux-thunk를 적용하는 코드를 작성해보자.

아 그리고 change_status 액션도 하나 더 만들었다.

 

redux-thunk 는 뭘 하는 미들웨어일까? (from. 벨로퍼트 깃북)

가장 간단히 설명하자면, 이 미들웨어는 객체 대신 함수를 생성하는 액션 생성함수를 작성 할 수 있게 해줍니다. 리덕스에서는 기본적으로는 액션 객체를 디스패치합니다. 일반 액션 생성자는, 다음과 같이 파라미터를 가지고 액션 객체를 생성하는 작업만합니다:

만약에 특정 액션이 몇초뒤에 실행되게 하거나, 현재 상태에 따라 아예 액션이 무시되게 하려면, 일반 액션 생성자로는 할 수가 없습니다. 하지만, redux-thunk 는 이를 가능케합니다.

간단하게 정리를 하자면 redux-thunk 는 일반 액션 생성자에 날개를 달아줍니다. 보통의 액션생성자는 그냥 하나의 액션객체를 생성 할 뿐이지만 redux-thunk 를 통해 만든 액션생성자는 그 내부에서 여러가지 작업을 할 수 있습니다. 이 곳에서 네트워크 요청을 해도 무방하죠. 또한, 이 안에서 액션을 여러번 디스패치 할 수도 있습니다.

 

 

예를 들어, 할일이 완료된 상태일 때만 삭제가 가능하게 만들고 싶다! 라고 한다면,

- 우선 리듀서에 '아이템 삭제 액션'이 왔을 때 어떻게 값을 변경할 지 구현해 놓는다.

- 그런데 나는 완료된 상태일 때만 삭제하는 액션을 생성할거니까 중간에서 '아이템 삭제 액션'을 생성할지 말지 if문으로 걸러야한다.

- 이때, thunk action을 사용한다!

- 그러면 전체흐름은 1. 삭제를 누르면 thunk action을 생성한다 2. 해당 액션에서 '아이템 삭제 액션'을 dispatch하거나 말거나 한다.

 

 

그럼 코드를 작성해보자!

 

1. 미들웨어를 적용하기 위해서 우선 redux-thunk를 설치해야한다. redux-toolkit도 설치하는 게 좋다.

npm install redux-thunk

 

2.  진짜 빡치는 부분 
원래는 useDispatch로 thunk action까지 만들 수 있었는데 지금은 안된단다;; (어이업써)
그래서 thunk액션을 위한 타입과 hook을 새로 만들어줘야한다.

완벽히 이해하지는 못했는데 stackoverflow에서 긁어왔다. 

 

긍데 뜬금없이 느끼는 거지만 vscode는 참 설명을 잘해주네 

 

(index.tsx)

import { configureStore } from '@reduxjs/toolkit' //리덕스 createStore()가 없어져서 이걸로 써야함
import { AnyAction } from 'redux'; 
import thunk, { ThunkDispatch } from 'redux-thunk';
import { Provider, useDispatch } from 'react-redux'; 
import rootReducer, { RootState } from './modules'

//thunk 미들웨어를 사용하고, 생성했던 루트리듀서를 사용하는 스토어를 만들어준다.
const store = configureStore({reducer: rootReducer, middleware: [thunk]});

//ThunkDispatch라는 함수형 인터페이스를 사용해야 thunk action을 dispatch(생성)할 수 있다.
export type AppThunkDispatch = ThunkDispatch<RootState, any, AnyAction>;

//thunk 액션에도 사용할 수 있는 hook인 useAppDispatch를 만든다...(문법은 잘 모르게씀)
export const useAppDispatch = () => useDispatch<AppThunkDispatch>();

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

root.render(
  //Provider의 파라미터로 전달
  <Provider store={store}>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </Provider>
);

reportWebVitals();

 

3. 액션 정의하기 (thunk action도)

import { todoItem } from '../../App'
import { Action, createAction} from "typesafe-actions";
import { ThunkAction } from 'redux-thunk';
import { RootState } from '..';

//Ation 정의 (리덕스 액션에 들어갈)
export const ADD_TODO = "todo/ADD_TODO";
export const CHANGE_STATUS = 'todo/CHANGE_STATUS'
export const DELETE_TODO = "todo/DELETE_TODO";

//Action 생성 함수 구현 
// 첫번째 인자: Action Type, 두번째 인자: payload(액션함수의 파라미터), 세번째 인자: Action Type인데 자동으로 적용
export const addTodo = createAction(ADD_TODO)<{todo: todoItem;}>();
export const changeStatus = createAction(CHANGE_STATUS)<{id : number;}>();
export const deleteTodo = createAction(DELETE_TODO)<{id: number;}>();

//thunk action 생성 함수 구현 
export const deleteTodoThunk = (id: number): ThunkAction<void, RootState, null, Action> => {
    return (dispatch, getState) => {
        const list = getState().todo.todo 
        //id가 해당 번호인 값의 상태가 참이면 삭제 dispatch
        list.forEach((val)=>{
            if((val.id === id)&&(val.status === true)){
                dispatch(deleteTodo({id : id}))
            }
        })   
    }
}

 

4. 원래 삭제버튼을 누르면 바로 '아이템 삭제 액션'을 dispatch 했지만 이제는 'thunk action'을 dispatch한다.

내가 따로 만든 thunk action용 useAppDispatch( )를 불러와서 사용해야한다.

import { useSelector } from 'react-redux';
import { RootState } from './modules';
import { useDispatch } from 'react-redux';
import { deleteTodo, deleteTodoThunk } from './modules/todos/actions';
//내가 만든 thunk action dispatch용 Hook을 불러온다
import { useAppDispatch } from '.';

function App() : React.ReactElement {

  const reduxTodoList = useSelector((state: RootState) => state.todo.todo)
  //thunk action용 dispatch Hook
  const dispatch = useAppDispatch();
  
  return (
    <div className="App">
      <Input/>
      {reduxTodoList.map((val: todoItem)=>
      <div style={{display: 'flex'}} key={val.id}>
        <Item 
          id={val.id}
          title = {val.title}
          date = {val.date}
          status = {val.status}
        />
        <button onClick={()=> {
         dispatch(deleteTodoThunk(val.id));
          }}>삭제</button>
      </div>
      )}
    </div>
  );
}

export default App;

 

끝!

흐름을 이해한 걸로 만족해보기로 한다...ㅎ