ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • React Component 가변적으로 생성/추가하기, 제거하기 (Alert 가변 생성 예시 포함)
    DEV/react 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

     

    댓글

Designed by Tistory.