Blur event doesn't occur well when I install million.js

54 Views Asked by At

My project is made with CRA(Create React App). I just watched a React lecture, and I installed million.js. https://million.dev/

But now, there's a serious problem with blur event handling. blur event never occurs. So, I tried to uninstall million.js, then blur event works...

Then my Question

  1. I did not use useRef hook in this project. If I use 'useRef', will I be able to overcome problem with blur event?
  2. Why blur event doesn't work in million.js envirionment?
  3. Except for this problems, what should I do to improve my codes?

The code below is part of my project with problem above. my github JPoint-project, https://github.com/Koh-Du-Beom/JPoint-project.

You can see my code in components/UserInfo/Userinfo.tsx. thank you for reading my article.

/*eslint-disable*/
import classes from '../../styles/FormStyles.module.css';
import ImageControler from '../ImageControler/ImageControler';
import { useState } from 'react';
import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState, AppDispatch } from '../../stores/redux/store';
import { updateUserInfo } from '../../stores/redux/userSlice';
import Divider from '../Divider/Divider';
import { isValidAccountNumber, isValidEmail, isValidName, isValidPhoneNumber, isValidStudentNumber, } from '../../utils/regularExpression/isValidUserInfo';

//million-ignore
const UserInfo : React.FC = () => {
    const [name, setName] = useState<string>('');
    const [grade, setGrade] = useState<string>('');
    const [major, setMajor] = useState<string>(''); //전공
    const [studentNumber, setStudentNumber] = useState<string>('');
    const [phoneNumber, setPhoneNumber] = useState<string>('');
    const [email, setEmail] = useState<string>('');
    
    const [bankAccount, setBankAccount] = useState<string>('');//계좌번호
    const [bankName, setBankName] = useState<string>(''); // 은행명
    const [bankBookImg, setBankBookImg] = useState<string>('');
    const [idCardImg, setIdCardImg] = useState<string>('');
    const [signImg, setSignImg] = useState<string>('');
    
    const [lastBlurTime, setLastBlurTime] = useState<number>(0);
    const [isValueChanged, setIsValueChanged] = useState<boolean>(false);
    const [errorMsg, setErrorMsg] = useState<{
        name?: string;
        grade?: string;
        major?: string;
        email?: string;
        phoneNumber?: string;
        studentNumber?: string;
        bankAccount?: string;
        bankName?: string;
    }>({});

    const dispatch = useDispatch<AppDispatch>();
    const userInfo = useSelector((state: RootState) => state.userInfo);

    const handleSaveButtonClick = () => {
        if(!isValueChanged){
            return;
        }
        setIsValueChanged(false);
        setLastBlurTime(0);
        
        dispatch(updateUserInfo({
            name: name,
      grade: grade,
      major: major,
      studentNumber: studentNumber,
      phoneNumber: phoneNumber,
      email: email,
      bankAccount: bankAccount,
      bankName: bankName,
      bankBookImg: bankBookImg,
      idCardImg: idCardImg,
      signImg: signImg,
        }));
        
  };

    const handleBlur = () => {
        setLastBlurTime(Date.now());
    }

    useEffect(() => {
        if (lastBlurTime === 0 || !isValueChanged) return;
        
        const timer = setTimeout(()=>{
            handleSaveButtonClick(); // 둘 다 비동기 함수지만 아래 코드가 먼저 실행될 수 있음. 그런 동작 막기위함
            setIsValueChanged(false);
        }, 1000); // 라우터(페이지 이동 시에도 실행되도록 하기)

        return () => clearTimeout(timer);
    }, [lastBlurTime, isValueChanged, handleSaveButtonClick])

    useEffect(() => {
    if (userInfo) {
      setName(userInfo.name);
      setGrade(userInfo.grade);
      setMajor(userInfo.major);
      setStudentNumber(userInfo.studentNumber);
      setPhoneNumber(userInfo.phoneNumber);
      setEmail(userInfo.email);
      setBankAccount(userInfo.bankAccount);
      setBankName(userInfo.bankName);
      setBankBookImg(userInfo.bankBookImg || '');
      setIdCardImg(userInfo.idCardImg || '');
      setSignImg(userInfo.signImg || '');
    }       
        
  }, [userInfo]);

    const handleName = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newName = event.target.value;
        setName(newName);
        setIsValueChanged(true);
    };

    const handleNameBlur = () => {
        
        if (!isValidName(name)){
            setErrorMsg((prev) => ({...prev, name : `${name? '올바른 이름이 아닙니다' : ''}`}));
            setName('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, name : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handleGrade = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newGrade = event.target.value;
        setGrade(newGrade);
        setIsValueChanged(true);
    };

    const handleGradeBlur = () => {
        if (!isValidStudentNumber(grade)){
            setErrorMsg((prev) => ({...prev, grade : `${grade? '올바른 학년이 아닙니다' : ''}`}));
            setGrade('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, grade : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handleMajor = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newMajor = event.target.value;
        setMajor(newMajor);
        setIsValueChanged(true);
    };

    const handleMajorBlur = () => {
        if(!isValidName(major)){
            setErrorMsg((prev) => ({...prev, major : `${major? '올바른 학과가 아닙니다' : ''}`}));
            setMajor('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, major : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handleStudentNumber = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newStudentNumber = event.target.value;
        setStudentNumber(newStudentNumber);
        setIsValueChanged(true);
    };

    const handleStudentNumberBlur = () => {
        if (!isValidStudentNumber(studentNumber)){
            setErrorMsg((prev) => ({...prev, studentNumber : `${studentNumber? '올바른 학번이 아닙니다' : ''}`}));
            setStudentNumber('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, studentNumber: undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handlePhoneNumber = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newPhoneNumber = event.target.value;
        setPhoneNumber(newPhoneNumber);
        setIsValueChanged(true);
    };

    const handlePhoneNumberBlur = () => {
        if (!isValidPhoneNumber(phoneNumber)){
            setErrorMsg((prev) => ({...prev, phoneNumber : `${phoneNumber? '올바른 연락처가 아닙니다' : ''}`}));
            setPhoneNumber('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, phoneNumber : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handleEmail = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newEmail = event.target.value;
        setEmail(newEmail);
        setIsValueChanged(true);
    };

    const handleEmailBlur = () => {
        if (!isValidEmail(email)){
            setErrorMsg((prev) => ({...prev, email : `${email? '올바른 이메일이 아닙니다' : ''}`}));
            setEmail('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, email : undefined}));
            setLastBlurTime(Date.now());
        }
    };


    const handleBankAccount = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newBankAccount = event.target.value;
        setBankAccount(newBankAccount);
        setIsValueChanged(true);
    };

    const handleBankAccountBlur = () => {
        if (!isValidAccountNumber(bankAccount)){
            setErrorMsg((prev) => ({...prev, bankAccount : `${bankAccount? '올바른 계좌번호가 아닙니다' : ''}`}));
            setBankAccount('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, bankAccount : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handleBankName = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newBankName = event.target.value;
        setBankName(newBankName);
        setIsValueChanged(true);
    };

    const handleBankNameBlur = () => {
        if(!isValidName(bankName)){
            setErrorMsg((prev) => ({...prev, bankName : `${bankName? '올바른 은행명이 아닙니다' : ''}`}));
            setBankName('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, bankName : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handlebankBookImg = (file : File | null) => {
        if(file){
            convertToBase64(file, setBankBookImg);
            setIsValueChanged(true);
        }else{
            setBankBookImg('');
        }
        handleBlur();
    };

    const handleIdCardImg= (file : File | null) => {
        if(file){
            convertToBase64(file, setIdCardImg);
            setIsValueChanged(true);
        }else{
            setIdCardImg('');
        }
        handleBlur();
    };

    const handleSignImg = (file : File | null) => {
        if(file){
            convertToBase64(file, setSignImg);
            setIsValueChanged(true);
        }else{
            setSignImg('');
        }
        handleBlur();
    };

    const convertToBase64 = (file : File, callback: (base64String : string) => void) => {
        const reader = new FileReader();
        reader.onload = () => {
            callback(reader.result as string);
        };
        reader.readAsDataURL(file); 
    };//fileReader 알아보기
    

    return (
        <form className={classes.container}>
            <div className={`${classes.wrapper} ${classes.end_double}`}>
                <div>
                    <div className={classes.big_title}>내 정보</div>
                </div>
            </div>
            <Divider/>      
            <div className={classes.big_title}>기본 정보</div>

            <div className={`${classes.wrapper} ${classes.double}`}>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>이름</div>
                        <input 
                            className={classes.input}
                            type='text'
                            onChange={handleName}
                            onBlur={handleNameBlur}
                            value={name}
                        />
                        {errorMsg.name && <div className={classes.errorMsg}>{errorMsg.name}</div>}
                    </div>
                </div>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>학과</div>
                        <input
                            className={classes.input} 
                            type='text'
                            onChange={handleMajor}  
                            onBlur={handleMajorBlur}
                            value={major}
                        />
                        {errorMsg.major && <div className={classes.errorMsg}>{errorMsg.major}</div>}
                    </div>
                </div>
            </div>

            <div className={`${classes.wrapper} ${classes.double}`}>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>학년</div>
                        <input 
                            className={classes.input}
                            type='text'
                            onChange={handleGrade}
                            onBlur={handleGradeBlur}
                            value={!grade ? '' : grade.toString()}
                        />
                        {errorMsg.grade && <div className={classes.errorMsg}>{errorMsg.grade}</div>}
                    </div>
                </div>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>학번</div>
                        <input
                            className={classes.input} 
                            type='text'
                            onChange={handleStudentNumber}  
                            onBlur={handleStudentNumberBlur}
                            value={studentNumber}
                        />
                        {errorMsg.studentNumber && <div className={classes.errorMsg}>{errorMsg.studentNumber}</div>}
                    </div>
                </div>
            </div>

            <div className={`${classes.wrapper} ${classes.double}`}>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>전화번호</div>
                        <input 
                            className={classes.input}
                            type='text'
                            onChange={handlePhoneNumber}
                            onBlur={handlePhoneNumberBlur}
                            value={phoneNumber}
                        />
                        {errorMsg.phoneNumber && <div className={classes.errorMsg}>{errorMsg.phoneNumber}</div>}
                    </div>
                </div>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>이메일</div>
                        <input
                            className={classes.input} 
                            type='text'
                            onChange={handleEmail}  
                            onBlur={handleEmailBlur}
                            value={email}
                        />
                        {errorMsg.email && <div className={classes.errorMsg}>{errorMsg.email}</div>}
                    </div>
                </div>
            </div>
            
            <Divider/>  
            
            <div className={classes.big_title}>통장 및 신분증사본</div>
            <div className={classes.wrapper}>
                <div>
                    <div className={classes.small_title}>통장사본</div>
                    <ImageControler onImageChange={handlebankBookImg} data={bankBookImg}/>
                </div>
            </div>
            <div className={`${classes.wrapper} ${classes.double}`}>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>계좌번호</div>
                        <input
                            className={classes.input} 
                            type='text'
                            onChange={handleBankAccount}    
                            onBlur={handleBankAccountBlur}
                            value={bankAccount}
                        />
                        {errorMsg.bankAccount && <div className={classes.errorMsg}>{errorMsg.bankAccount}</div>}
                    </div>
                </div>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>은행명</div>
                        <input 
                            className={classes.input}
                            type='text'
                            onChange={handleBankName}
                            onBlur={handleBankNameBlur}
                            value={bankName}
                        />
                        {errorMsg.bankName && <div className={classes.errorMsg}>{errorMsg.bankName}</div>}
                    </div>
                </div>
            </div>


            <div className={classes.wrapper}>
                <div>
                    <div className={classes.small_title}>신분증사본</div>
                    <ImageControler onImageChange={handleIdCardImg} data={idCardImg}/>
                </div>
            </div>

            <div className={classes.wrapper}>
                <div>
                    <div className={classes.small_title}>서명 사진</div>
                    <ImageControler onImageChange={handleSignImg} data={signImg}/>
                </div>
            </div>
        </form>
    )
}

export default UserInfo;

My project is made with CRA(Create React App). I just watched a React lecture, and I installed million.js. But now, there's a serious problem with blur event handling. blur event never occurs. So, I tried to uninstall million.js, then blur event works...

Then my Question

  1. I did not use useRef hook in this project. If I use 'useRef', will I be able to overcome problem with blur event?
  2. Why blur event doesn't work in million.js envirionment?

The code below is part of my project with problem above. my github JPoint-project, https://github.com/Koh-Du-Beom/JPoint-project.

You can see my code in components/UserInfo/Userinfo.tsx. thank you for reading my article.

0

There are 0 best solutions below