본문 바로가기
Projects/개인 포트폴리오 사이트 만들기

[ React ] hover 구현하기

by ウリ김영은 2024. 1. 22.
useEffect(() => {
	formCoverRef?.current?.addEventListener('mouseEnter', () => {
		setIsHovered(true);
	});
	formCoverRef?.current?.addEventListener('mouseLeave', () => {});
	setIsHovered(false);
}, []);

 

 

위의 함수는 해당 화면에 마우스를 올리면 로그인하기 버튼이 나오게 하기 위해 만든 함수였다. 

(버튼 디자인이 저건 아니다.)

 

근데 동작을 하지 않아서 구글링을 해봤다. 

 

처음에 찾은 문제점은 dependency였다. 

 

위의 로직대로라면 

컴포넌트가 마운트되고 나서 한번 실행되는데, 그때는 아직 formCoverRef를 <FormCover> 컴포넌트에 주기 전이니 당연히 undefined이다. 

그리고 undefined 이기 때문에 addEventListener는 실행되지 않고 useEffect 함수는 끝난다. 

 

그래서 dependency에 formCoverRef 를 줬다. undefined 에서 컴포넌트로 바뀌었을 때 다시 실행될 수 있게 하기 위해서. 

 

useEffect(() => {
	formCoverRef?.current?.addEventListener('mouseEnter', () => {
		setIsHovered(true);
	});
	formCoverRef?.current?.addEventListener('mouseLeave', () => {});
	setIsHovered(false);
}, [formCoverRef]);

 

이렇게 문제 해결!

이 될 줄 알았지만 동작하지 않았다. 

 

 

두 번째 방법은 react 의 이벤트 사용하기 

https://velog.io/@dacircle/React-hover%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0

{!isAuthorized && (
	<FormCover
		className="hover:bg-white/100"
		onMouseOver={() => {
			setIsHovered(true);
		}}
		onMouseOut={() => setIsHovered(false)}
	>
		{isHovered && (
			<Button
				onClick={buttonClickHandler}
				className="min-w-[18rem] w-[24vw] mb-[1.6rem] py-3 bg-transparent text-2xl"
			>
				로그인하기
			</Button>
		)}
	</FormCover>
)}

 

 

 

세번째 방법

useHover 커스텀훅 만들기 

https://ghost4551.tistory.com/178

 

import { useCallback, useEffect, useState, useRef } from 'react';

const useHover = () => {
	const [state, setState] = useState(false);
	const ref = useRef(null);

	const handleMouseOver = useCallback(() => setState(true), []);
	const handleMouseOut = useCallback(() => setState(false), []);

	useEffect(() => {
		const element = ref.current;
		if (!element) return;

		element.addEventListener('mouseover', handleMouseOver);
		element.addEventListener('mouseout', handleMouseOut);

		// useEffect에서 이벤트를 등록할 때는
		// 꼭 정리(clean-up)를 해줘야한다.
		return () => {
			element.removeEventListener('mouseover', handleMouseOver);
			element.removeEventListener('mouseout', handleMouseOut);
		};
		// 위에서 handleMouseOver, handleMouseOut useCallback을 통해 메모리제이션을 이용하여
		// 의존성에 굳이 넣어줄 필요가 없지만 추후에 생성되는 로직이나 이벤트에서 handle 관련 함수 로직이
		// 바뀔 가능성을 염두해서 의존성에 넣어준다.
	}, [ref, handleMouseOver, handleMouseOut]);

	return [ref, state];
};

export default useHover;

 

 

네번째 방법 : 라이브러리 사용하기

https://usehooks.com/usehover

근데 동작을 안 한다..? 

다른 문제가 있는지 봐야할 것 같다. 

 

전혀 다른 문제였던 것 같다. 

원래 Button 컴포넌트를 children으로 받아서 Form 컴포넌트에서 사용했었는데, 

로직을 둘러보다가 굳이 Button 으로 내려줘야 할 특별한 이유가 없어서 

그냥 FormCover 컴포넌트 안으로 넣어줬다. 

import { ComponentProps } from 'react';
import Button from '../Button';
import useHover from '@/hooks/useHover';

interface Props extends ComponentProps<'div'> {
	buttonClickHandler: () => void;
}
export default function FormCover({ className, buttonClickHandler }: Props) {
	const [ref, hovering] = useHover();
	return (
		<div className={className +
			' absolute z-[999] bg-white/90 w-full h-full top-0 left-0 center font-header bg-white/30 rounded-md '
			}
		ref={ref}>
		{hovering && (
			<Button
				onClick={buttonClickHandler}
				className="min-w-[18rem] w-[24vw] mb-[1.6rem] py-3 bg-transparent text-2xl">
			로그인하기
			</Button>
		)}
		</div>
	);
}

 

그러니까 동작을 한다. 

 

라이브러리를 사용해도 , 커스텀 훅을 사용해도.. (둘이 로직 같은 것 같은 것 같다)

 

일단 문제는 해결돼서 좋긴한데, 

왜 동작을 하는지에 대해 아직 이해를 못해서 

이에 대해서 좀 고민해봐야 할 것 같다. 

 

혹시 아시는 분 있으면 언제든지 알려주시면 감사하겠습니다

 


참고

https://stackoverflow.com/questions/54346040/react-hooks-ref-is-not-available-inside-useeffect

https://velog.io/@dabin-lee/useRef%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EB%A6%AC%EC%8A%A4%EB%84%88