1. customHistory 만들어서 적용하기

프로젝트를 개발하다보면, thunk 함수 내에서 라우터를 사용해야 될 때도 있다. 예를 들자면 로그인 요청을 하는데 로그인이 성공 할 시 / 경로로 이동시키고, 실패 할 시 경로를 유지 하는 형태로 구현 할 수도 있는데

그러한 상황엔 어떻게 구현을 해야하는지 알아보도록 하겠다.

index.js

thunk 에서 라우터의 history 객체를 사용하려면, BrowserHistory 인스턴스를 직접 만들어서 적용해야 한다.  그리고, redux-thunk 의 withExtraArgument 를 사용하면 thunk함수에서 사전에 정해준 값들을 참조 할 수 있다. 스토어를 만드는 코드를 다음과 같이 변경하면된다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './modules';
import logger from 'redux-logger';
import { composeWithDevTools } from 'redux-devtools-extension';
import ReduxThunk from 'redux-thunk';
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';

const customHistory = createBrowserHistory();

const store = createStore(
  rootReducer,
  // logger 를 사용하는 경우, logger가 가장 마지막에 와야 한다.
  composeWithDevTools(
    applyMiddleware(
      ReduxThunk.withExtraArgument({ history: customHistory }),
      logger
    )
  )
); // 여러개의 미들웨어를 적용 할 수 있다.

ReactDOM.render(
  <React.StrictMode>
    <Router history={customHistory}>
      <BrowserRouter>
        <Provider store={store}>
          <App />
        </Provider>
      </BrowserRouter>
    </Router>
  </React.StrictMode>,
  document.getElementById('root')
);

 

2. 홈 화면으로 가는 thunk 만들기

이제 홈 화면으로 가는 thunk를 작성해보도록 하겠다.

해당 함수를 posts 모듈에 추가 해준다.

modules/posts.js - goToHome

// 3번째 인자를 사용하면 withExtraArgument 에서 넣어준 값들을 사용 할 수 있다.
export const goToHome = () => (dispatch, getState, { history }) => {
  history.push('/');
};

 

이제 PostContainer.js 에서 이 thunk 를 디스패치한다.

containers/PostContainer.js

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { getPost, goToHome } from '../modules/posts';
import Post from '../components/Post';

function PostContainer({ postId }) {
  const { data, loading, error } = useSelector(
    state => state.posts.post[postId]
  ) || {
    loading: false,
    data: null,
    error: null
  }; // 아예 데이터가 존재하지 않을 때가 있으므로, 비구조화 할당이 오류나지 않도록
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(getPost(postId));
  }, [dispatch, postId]);

  if (loading && !data) return <div>로딩중...</div>; // 로딩중이고 데이터 없을때만
  if (error) return <div>에러 발생!</div>;
  if (!data) return null;

  return (
    <>
      <button onClick={() => dispatch(goToHome())}>홈으로 이동</button>
      <Post post={data} />
    </>
  );
}

export default PostContainer;

 

+ Recent posts