antd라이브러리로 모달 구현하기
전역 변수로 관리하기 위해서 먼저 redux-toolkit와 react redux를 설치합니다.
npm i @reduxjs/toolkit react-redux
이후 store와 Provider를 등록합니다 (BrowserRouter는 페이지 이동을 하기 위해 작성한 react-router 관련 코드입니다.)
import { store } from './store/redux';
import { Provider } from 'react-redux';
root.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);
이후 모달의 기능을 구현할 slice를 등록합니다.
import { createSlice } from '@reduxjs/toolkit';
import { ReactNode } from 'react';
import { RootState } from '.';
export interface ModalState {
isModalOpen: boolean;
modalComponent: ReactNode | null;
}
const initialState: ModalState = {
isModalOpen: false,
modalComponent: null,
};
export const modalSlice = createSlice({
name: 'modal',
initialState,
reducers: {
openModal: (state, action) => {
state.isModalOpen = true;
state.modalComponent = action.payload;
},
closeModal: (state) => {
state.isModalOpen = false;
state.modalComponent = null;
},
},
});
export const { openModal, closeModal } = modalSlice.actions;
export const isModalOpen = (state: RootState) => state.modal.isModalOpen;
export const modalComponent = (state: RootState) => state.modal.modalComponent;
export default modalSlice.reducer;
타입스크립트가 적용된 modalSlice입니다.
혹시 redux-toolkit에 대해서 간단하게 학습하고 싶다면
npx create-react-app my-app --template redux-typescript
을 통해서 간단한 counter를 활용한 예시코드를 확인할 수 있습니다. (my-app 대신 자기가 만들고 싶은 프로젝트 이름)
api 요청 없는 전역변수이기에 extrareducer와 thunk는 사용하지 않았습니다.
이후 동일한 폴더내 있는 index.ts에 configureStore를 만들었습니다. 이를 통해서 index.tsx 에 등록한 store에 modalReducer를 등록했습니다.
import { configureStore } from '@reduxjs/toolkit';
import modalReducer from './modalSlice';
export const store = configureStore({
reducer: {
modal: modalReducer,
},
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
usesDispatch와 useSelector 관련 코드는 hooks 폴더에서 작성 했습니다.
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from '../store/redux';
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
최종적으로 modalSlice에서 export했던 모달상태(isModalOpen)과 모달 내에 작성할 컴퍼넌트(modalComponent)를 모달에 등록합니다.
import { Modal } from 'antd';
import { useAppDispatch, useAppSelector } from '../../hooks/useReduxToolkit';
import { closeModal, isModalOpen, modalComponent } from '../../store/redux/modalSlice';
const CommonModal = () => {
const modalState = useAppSelector(isModalOpen);
const modalComp = useAppSelector(modalComponent);
const dispatch = useAppDispatch();
const onCloseModal = () => dispatch(closeModal());
return (
<div>
<Modal open={modalState} footer={null} onCancel={onCloseModal} keyboard centered maskClosable>
{modalComp}
</Modal>
</div>
);
};
export default CommonModal;
이후에 모달을 사용할 컴퍼넌트에서 useAppDispatch와 openModal 함수를 통해서 모달을 열면 됩니다.
import { useAppDispatch } from '../hooks/useReduxToolkit';
import { openModal } from '../store/redux/modalSlice';
import Login from '../components/auth/Login';
const Header = () => {
const dispatch = useAppDispatch();
const modalOpen = () => {
dispatch(openModal(<Login />));
};
return (
<HeaderContainer>
<div>logo</div>
<div onClick={modalOpen}>로그인</div>
</HeaderContainer>
);
};
export default Header;
이렇게 하면 전역변수로 관리했기 때문에, 모달이 state 변경에 의해서 주변 컴퍼넌트를 리랜더링 시키지 않습니다.
아직 모달에 스타일 적용은 하지 않았습니다
++++
뒤늦게 알았는데 이럴 경우 병렬화 에러가 떴습니다. 해결방법은 https://ungumungum.tistory.com/25