ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Input 이벤트] 포커스 이벤트, 영역 외부 클릭시 상태 변화, document.addEventListener
    React/과제 테스트 2023. 8. 8. 18:53

     

    과제 테스트 준비

    input 태그와 관련되어서 좀 더 심도있게 준비를 하게 되었다. 

     

     

    🐇 포커스 이벤트

    유저가 input 태그를 클릭하면 아래 키패드가 떠야 한다. 

     

    1. 키패드 가시성 여부를 state로 관리한다. 

    const [IsOpenPasswordKeypad, setIsOpenPasswordKeypad] = useState(false);

    2. 두 개의 같은 동작을 이용할 예정으로 키패드는 컴포넌트로 생성한다. 

    const displayKeypad = (type: InputType) => {
    return (
    <>
    {Array.from({ length: 10 }, (_, i) => (
    <Button
    key={i}
    data-testid={i}
    css={{ width: '20%', margin: 20 }}
    variant="secondary"
    onClick={() => handlePasswordChange(type, i)}
    >
    {i}
    </Button>
    ))}
    <Button
    key="blank"
    data-testid="blank"
    css={{ width: '20%', margin: 20, color: 'transparent' }}
    variant="secondary"
    >
    9
    </Button>
    <Button key="blank" data-testid="blank" css={{ width: '20%', margin: 20 }} variant="secondary">
    *
    </Button>
    </>
    );
    };

    키패드 모양

    3. div 클릭 / focus일때 보여준다.

    <div onFocus={() => setIsOpenPasswordKeypad(true)} >

     

     

    🥕 영역  외부를 클릭하면 키패드가 보이면 안된다. 

    ※ document.addEventListener

    document.removeEventListener는 브라우저의 Document 객체에 대한 이벤트 리스너를 추가하고 제거하는 메소드입니다. 이 두 메소드를 사용하여 웹 페이지에서 특정 이벤트가 발생했을 때 원하는 동작을 수행하거나 이벤트 리스너를 제거할 수 있습니다.

     

     

    Syntax

    eventTarget.addEventListener('eventType', function)

    -> 이벤트를실행할타겟.addEventListener('이벤트타입', 실행할함수)

    • eventTarget(이벤트 타겟)은 해당 이벤트를 적용할 DOM을 가져와 준다.
    • eventType(이벤트 타입)은 말 그대로 어떤 타입의 이벤트를 적용할 것인지 써주면 된다. 대표적으로 click DOMContentLoad, scroll submit등등이 있다. 타입을 써줄 때는 따옴표를 잊지 말고 감싸줘야 한다.
    • click: 요소를 클릭하면 이벤트 발생.
    • DOMContentLoaded: 페이지가 새로 로딩될 때마다 이벤트 발생.
    • scroll: 스크롤을 하면 이벤트 발생.
    • submit: 양식을 전송하면 이벤트 발생. (ex: input type이 submit일 때 submit 버튼을 누르는 것과 같음)
    • function(실행할함수)에는 이벤트를 발생시켰을 시 실행할 동작을 가져와 준다.

     

    예시 - React

    1. useRef 사용

    const passwordRef = useRef<HTMLDivElement>(null);
     
     
    <div onFocus={() => setIsOpenPasswordKeypad(true)} ref={passwordRef}>
    <Input label="비밀번호" bottomText={description}>
    <Input.TextField value={password} readOnly  />
    </Input>
    {IsOpenPasswordKeypad && displayKeypad('password')}
    </div>

    2. useEffect로 이벤트 리스너 추가

    useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
    if (passwordRef.current && !passwordRef.current.contains(event.target as Node)) {
    setIsOpenPasswordKeypad(false);
    }
    if (confirmPasswordRef.current && !confirmPasswordRef.current.contains(event.target as Node)) {
    setIsOpenConfirmPasswordKeypad(false);
    }
    };

    document.addEventListener('mousedown', handleClickOutside);
    return () => {
    document.removeEventListener('mousedown', handleClickOutside);
    };
    }, []);

     

    [설명]

    위의 코드는 React의 useEffect 훅을 사용하여 컴포넌트가 렌더링될 때 mousedown 이벤트 리스너를 추가하고, 컴포넌트가 언마운트될 때 해당 이벤트 리스너를 제거합니다. []로 빈 배열을 전달하여 useEffect의 두 번째 인자로 빈 배열을 주었기 때문에 컴포넌트가 언마운트될 때만 이벤트 리스너가 제거됩니다.

     

    [목적]

    이렇게 하는 이유는 메모리 누수를 방지하고 이벤트 리스너가 불필요하게 유지되는 것을 막기 위해서입니다. 만약 이벤트 리스너가 컴포넌트가 계속 렌더링될 때마다 추가되면, 같은 종류의 이벤트 리스너가 계속 누적되어 메모리 낭비와 예기치 않은 동작을 초래할 수 있습니다. 이를 방지하기 위해 컴포넌트가 마운트되면 이벤트 리스너를 추가하고, 언마운트될 때 제거하는 패턴을 사용합니다.

     

    💡 passwordRef.current.contains(event.target as Node) 표현식

         passwordRef.current에 포함된 자식 요소인지를 검사하여 외부 클릭을 판별합니다.

     

    💡 click 대신 mouseDown을 사용한 이유

      click mousedown
    발생 시점 버튼 클릭 시 발생 마우스 버튼을 누를 때 발생
    트리거 버튼 뗀 후 발생 시점
    결론 팝업, 모달 (키패드)을 열고 닫을 때는 mousedown이 더 적합하다.

     팝업

    사용자가 클릭한 시점에서 나타나야 하고, 팝업 외부를 클릭한 순간 닫혀야 합니다.

    mousedown 이벤트를 사용하면 사용자가 마우스 버튼을 누른 순간에 이벤트를 트리거할 수 있어 이런 동작에 적합합니다.

    또한, 클릭 이벤트의 경우 사용자가 버튼을 클릭하고 있을 때 (마우스 버튼을 누른 상태에서) 팝업이 열려있다면, 버튼을 떼기 전까지는 팝업이 열려있을 가능성이 있습니다. 

     

     

    예시 - JavaScript 

    const button = document.querySelector('button');
        //1번
        const print = () => {
        	console.log('print!');
        }
    	button.addEventListener('click', print);
Designed by Tistory.