React

[React] redux 를 이용하여 popup 창 구현하기 - 떽떽대는 개발공부

떽이 2021. 4. 15. 15:28

 

 

 

2021.01.11 - [React] - [React] ReactDOM 을 사용하여 팝업창 띄우기 - 떽떽대는 개발공부

 

[React] ReactDOM 을 사용하여 팝업창 띄우기 - 떽떽대는 개발공부

오늘은 react-dom 을 사용하여 팝업창 생성을 해보도록 하자. react 프로젝트가 생성될 때 하나의 돔이 생성되고 그 위에 view 가 그려진다. 따라서 팝업창을 생성하기 위해선 이미 펼져쳐 있는 dom 위

ddeck.tistory.com

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 을 사용하여 팝업창 띄우기 - 떽떽대는 개발공부

 

[React] ReactDOM 을 사용하여 팝업창 띄우기 - 떽떽대는 개발공부

오늘은 react-dom 을 사용하여 팝업창 생성을 해보도록 하자. react 프로젝트가 생성될 때 하나의 돔이 생성되고 그 위에 view 가 그려진다. 따라서 팝업창을 생성하기 위해선 이미 펼져쳐 있는 dom 위

ddeck.tistory.com