728x90

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

// Use throughout your app instead of plain `useDispatch` and `useSelector`
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

+ Recent posts