DEV/react

React Component 가변적으로 생성/추가하기, 제거하기 (Alert 가변 생성 예시 포함)

석봉 2024. 2. 19. 11:00

* 컴포넌트 동적으로 생성/추가(가변적으로 생성)하는 코드만 필요한 경우 앞 Alert 코드는 넘어가도록 하자.


 

회원가입 및 로그인 기능을 구현하다 보니 로그인, 회원가입에 대한 Alert이 필요했다.

(성공, 실패 여부. 에러 및 실패에 대한 사유 등 알림을 띄워주기 위해 필요.)

 

Alert Component를 하나 만들고 로그인 및 회원가입 API Response에 따라 Alert을 가변적으로 생성하려고 한다.

 

우선 Alert 코드를 다음과 같이 생성하였다.

 

Alert.js

import style from '../../style/component/Alert.module.css'
import React, { useRef, useEffect } from 'react';

/**
 * [Alert]
 * @param alertType {String}    "success", "warning", "error" 중 택 1, 지정하지 않거나 다른 값을 넣는 경우 일반 테마 적용됨
 * @param contents {String}     Alert 내용
 * @param duration {Number}     Alert 자동 종료 시간(Sec), 값이 없는 경우 종료하지 않음
 * @returns {Element}
 */
const Alert = ({alertType, contents, duration}) => {
    // Alert style을 가변적으로 지정하기 위해 사용
    let styleType;
    // Dom 선택을 위해 Ref 생성
    const alertRef = useRef(null);

    // Alert style 지정
    styleType = `${style.alertContainer} `;
    if(alertType === 'success') { styleType = styleType + `${style.success}`; }
    else if(alertType === 'warning') { styleType = styleType + `${style.warning}`; }
    else if(alertType === 'error') { styleType = styleType + `${style.error}`; }
    else { styleType = styleType + `${style.normal}`; }

    /**
     * [Alert 제거]
     *  - Alert Component에서 parameter로 duration을 받은 경우 duration 이후 자동 종료됨.
     *  - duration에 상관없이 x 아이콘을 눌렀을 때 또한 종료됨.
     */
    const closeAlert = () => {
        const elementToRemove = alertRef.current;
        // duration 이전에 alert 종료 버튼을 먼저 누르는 경우 해당 노드가 없어 에러가 발생하여 조건문 추가
        if (elementToRemove && elementToRemove.parentNode) {
            elementToRemove.parentNode.removeChild(elementToRemove);
        }
    };

    useEffect(() => {
        if (duration) {
            /**
             * setTimeout으로 duration 타임 이후 해당 alert 제거하는 함수 호출
             * duration : 자동 종료 시간, 단위 Sec로 자동 변경
             */
            const timeoutId = setTimeout(() => {
                closeAlert();
            }, duration * 1000);
            return () => clearTimeout(timeoutId);
        }
    }, [duration]);

    return (
        <div className={styleType} ref={alertRef}>
            {/* Contents */}
            <div>
                <label>
                    {contents}
                </label>
            </div>
            {/* X button */}
            <div onClick={closeAlert}>
                <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" focusable="false" viewBox="0 0 12 12" aria-hidden="true">
                    <path stroke="currentColor" strokeLinecap="round" d="M3 9l6-6m0 6L3 3"></path>
                </svg>
            </div>
        </div>
    );
};

export default Alert;

 

Alert.module.css

.alertContainer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-inline: 10px;
    padding-block: 8px;
    box-sizing: border-box;
    width: 100%;
    border-radius: 8px;
    margin-bottom: 5px;
}

.success {
    background-color: rgb(237, 248, 244);
    border: 1px solid rgb(89 184 147);
    color: rgb(24, 97, 70);
}

.warning {
    background-color: rgb(255, 247, 237);
    border: 1px solid rgb(254, 214, 168);
    color: rgb(173, 89, 24);
}

.error {
    background-color: rgb(255, 240, 241);
    border: 1px solid rgb(245, 181, 186);
    color: rgb(140, 35, 44);
}

.normal {
    background-color: rgb(248, 249, 249);
    border: 1px solid rgb(216, 220, 222);
    color: rgb(104, 115, 125)
}

 

그리고 해당 Alert Component를 동적으로 생성이 필요한 곳으로 이동하여 다음과 같이 사용해보자.

 

LoginPage.js (나는 여기서 Alert을 생성하고자 함)

    // ... LoginPage 코드 생략
    // Alert
    const [alerts, setAlerts] = useState([]);

    const addAlert = (alertType, contents, duration) => {
        const newAlert = <Alert
            key={alerts.length}
            alertType={alertType}
            contents={contents}
            duration={duration}
        />;

        setAlerts([...alerts, newAlert]);
    }
    
    // ...LoginPage 코드 생략
    // API 호출 후 Response를 받는 부분...
    	addAlert('warning', response.data.message, 3);
    
    // API 예외처리 부분...
    } catch(error) {
	    addAlert('error', error, 3);
    }
    
    // ... LoginPage 코드 생략
    return (
        <div className={style.backgroundContainer}>
            {/* alert을 넣어 줄 Container */}
            <div className={style.alertStackContainer}>
                {alerts.map((alert, index) => (
                    <div key={index}>{alert}</div>
                ))}
            </div>
            // ... 이후 코드 생략

 

LoginPage.module.css

/* 그 외 css 생략 */

.alertStackContainer {
    position: absolute;
    right: 10px;
    top: 10px;
    display: flex;
    flex-direction: column;
    width: 500px;
    border-radius: 10px;
    z-index: 1;
}

 

useState로 alerts 배열을 생성하고 addAlert 함수를 통해 Alert Component를 저장한다.

 

LoginPage를 구성하고 return해 주는 부분에 Alert을 띄워줄 공간을 만들어 두었다. (alertStackContainer)

 

{alerts.map((alert, index) => (
    <div key={index}>{alert}</div>
))}

 

위와 같이 useState로 관리되는 alerts를 출력해 준다.

 

useState에 대한 개념이 부족하다면 하단 게시물을 참고하자.

https://seokbong.tistory.com/243

 

React Hooks 간단 정리

useState - 현재 상태를 나타내는 state와 state를 변경하는 setState를 한 쌍으로 제공 - state의 초기값 설정 가능, 첫 렌더링 때 한번 사용 useEffect - useEffect(function, deps) 형태로 사용 - function은 실행시킬

seokbong.tistory.com

 


관련 게시물

https://seokbong.tistory.com/253