React
[React] redux 를 이용하여 popup 창 구현하기 - 떽떽대는 개발공부
떽이
2021. 4. 15. 15:28
2021.01.11 - [React] - [React] ReactDOM 을 사용하여 팝업창 띄우기 - 떽떽대는 개발공부
redux 를 이용하여 react 에서 popup 창 구현하는 포스팅이다. (typescript 사용)
redux 를 이용하기 때문에 action, reducer 를 구현해야 한다.
src/modules/common/layer.ts
import { Action, createAction, handleActions } from 'redux-actions'
export interface ILayerState {
[key: string]: boolean
}
export const LAYER = 'CommonLayer'
const OPEN = `${LAYER}/OPEN`
const CLOSE = `${LAYER}/CLOSE`
export const layerActions = {
open: createAction(OPEN),
close: createAction(CLOSE),
}
const initialState = {}
export const reducer = {
[OPEN]: (state: ILayerState, action: Action<string>) => ({
...state,
[action.payload]: true,
}),
[CLOSE]: (state: ILayerState, action: Action<string>) => ({
...state,
[action.payload]: false,
}),
}
export const layerReducer = handleActions(reducer, initialState)
생성된 action 과 리듀서를 store 에 넣어준다.
store, middleware 적용은 이 포스팅에선 생략한다.
src/modules/index.ts
import { combineReducers } from 'redux'
import { StateType } from 'typesafe-actions'
import { LAYER, layerReducer } from '@/modules/common/layer'
export const rootReducer = combineReducers({
[LAYER]: layerReducer,
})
export type IRootState = StateType<typeof rootReducer>
이제 팝업될 layer 를 구현할 것이다.
src/utils/domUtils.ts
import { MouseEvent } from 'react'
export const isDimmedClick = (e: MouseEvent<Element>, className: string) => {
const { target } = e
return (target as any).classList.contains(className)
}
src/components/common/layer/Wrapper.tsx
import React, { MouseEvent, ReactNode } from 'react'
import { isDimmedClick } from '@/utils/domUtils'
interface IWrapperProps {
children: ReactNode
isOpen: boolean
close: () => void
}
export const Wrapper = (props: IWrapperProps) => {
const wrapperClassName = 'ly'
const onClick = (e: MouseEvent<HTMLDivElement>) => {
if (isDimmedClick(e, wrapperClassName)) {
props.close()
}
}
return (
<div
className={wrapperClassName}
onClick={onClick}
style={{ display: props.isOpen ? 'block' : 'none' }}
>
{props.children}
</div>
)
}
src/components/common/layer/Leave.tsx
import React, { ReactNode } from 'react'
interface ILeaveProps {
children: ReactNode
}
export const Leave = (props: ILeaveProps) => {
return <div className="ly-leave">{props.children}</div>
}
src/components/common/layer/Message.tsx
import React, { ReactNode } from 'react'
interface IMessageProps {
children: ReactNode
}
export const Message = (props: IMessageProps) => {
return <p className='ly-leave__msg'>{props.children}</p>
}
src/components/common/layer/ButtonBox.tsx
import React, { ReactNode } from 'react'
interface IButtonBoxProps {
children: ReactNode
}
export const ButtonBox = (props: IButtonBoxProps) => {
return <div className="ly__btn-box">{props.children}</div>
}
src/components/common/layer/Button.tsx
import React, { MouseEventHandler, ReactNode } from 'react'
interface IButtonProps {
onClick: MouseEventHandler<HTMLButtonElement>
children?: ReactNode
}
export const Button = (props: IButtonProps) => {
return (
<button type="button" onClick={props.onClick} className='ly__btn'>
{props.children}
</button>
)
}
src/components/common/PopupLayer.tsx
import React, { useCallback } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { IRootState } from '@/modules'
import { ILayerState, LAYER, layerActions } from '@/modules/common/layer'
import * as Layer from '@/components/common/layer'
export enum LEAVE_ALERT_TYPE {
TEST_POPUP = 'TEST_POPUP',
}
export const TestPopupLayer = (props: { useTransition?: boolean }) => {
const dispatch = useDispatch()
const layerStatus = useSelector<IRootState, ILayerState>(state => state[LAYER])
const isOpen = layerStatus[LEAVE_ALERT_TYPE.TEST_POPUP]
const onCancel = useCallback(() => dispatch(layerActions.close(LEAVE_ALERT_TYPE.TEST_POPUP)), [
dispatch,
])
return (
<Layer.Wrapper isOpen={isOpen} close={onCancel}>
<Layer.Leave>
<Layer.Message>{('layer popup 내용입니다.')}</Layer.Message>
<Layer.ButtonBox>
<Layer.Button onClick={onCancel}>
{('close')}
</Layer.Button>
</Layer.ButtonBox>
</Layer.Leave>
</Layer.Wrapper>
)
}
이제 popup 창이 필요한 컴포넌트에서 구현 해주면 된다.
src/components/Temp.tsx
import React from 'react'
import { useDispatch } from 'react-redux'
import { TestPopupLayer, LEAVE_ALERT_TYPE } from '@/components/common/PopupLayer'
import { layerActions } from '@/modules/common/layer'
export const Temp = () => {
const dispatch = useDispatch()
const onClick = () => {
dispatch(layerActions.open(LEAVE_ALERT_TYPE.TEST_POPUP))
}
return (
<>
<button onClick={onClick}>popup Layer</button>
<TestPopupLayer />
</>
)
}
css 는 아래와 같다.
.ly {
display: none;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 10;
background-color: rgba(0, 0, 0, 0.4);
}
.ly-leave {
position: relative;
top: 50%;
max-width: 259px;
margin: 0 auto;
padding: 30px 30px 90px;
background-color: #fff;
border-radius: 10px;
-webkit-transform: translate(0, -50%);
transform: translate(0, -50%);
-webkit-box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.08);
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.08);
}
.ly-leave__msg {
font-size: 1.6rem;
line-height: 1.9rem;
color: #333;
}
이렇게 하면 아래와 같은 결과 페이지가 나타나게 된다.
2021.01.11 - [React] - [React] ReactDOM 을 사용하여 팝업창 띄우기 - 떽떽대는 개발공부