다음과 같이 input에 focus가 되면 검색 결과창이 나타나고 바깥 영역을 클릭하면 결과창이 없어지도록 해보자.
리액트에서 focus 관련 이벤트를 처리하려면 onFocus, onBlur 속성에 핸들러를 전달하면 된다.
input에 focus가 되었는지를 나타내는 state인 isInputFocused
를 만들고 관련 핸들러를 만들어 onFocus, onBlur에 전달해주었다.
그리고 isInputFocused
에 따라 검색 결과창인 SearchRecommend
를 조건부 렌더링했다.
export function Search() {
const [isInputFocused, setIsInputFocused] = useState(false);
const setRecommendVisible = () => setIsInputFocused(true);
const setRecommendInvisible = () => setIsInputFocused(false);
/* ... */
return (
<div>
<SearchForm
keyword={keyword}
onChange={handleKeywordChange}
onFocus={setRecommendVisible}
onBlur={setRecommendInvisible}
/>
{isInputFocused && <SearchRecommend />}
</div>
);
}
export function SearchForm(props: SearchFormProps) {
return (
<form>
<InputBase>
<InputWithIcon>
<FaSearch />
<Input
placeholder="질환명을 입력해주세요."
value={props.keyword}
onChange={props.onChange}
onFocus={props.onFocus}
onBlur={props.onBlur}
/>
</InputWithIcon>
<Button>검색</Button>
</InputBase>
</form>
);
}
처음에는 toggleRecommendVisible
을 만들어서 state를 !isInputFocused
로 업데이트를 했는데 예기치 못한 문제가 있을 수 있으니 focus, blur별로 따로 이벤트 핸들러를 만들어서 처리하는 것이 더 맞다고 생각했다.
검색 결과창을 state값에 따라 조건부 렌더링하는 것으로 했는데 생각해보니 css 속성인 display: none;
으로도 검색 결과창을 안보이게 할 수도 있다.
export const Container = styled.div<{ $isVisible: boolean }>`
display: ${(props) => (props.$isVisible ? 'block' : 'none')};
margin-top: 20px;
background-color: white;
border-radius: 30px;
height: 400px;
box-shadow: 0px 0px 7px -3px #bcbcbc;
`;
export function SearchRecommend(props: SearchRecommendProps) {
return (
<Container $isVisible={props.isVisible}>
{/* ... */}
</Container>
);
}
export function Search() {
/* ... */
return (
<div>
<SearchForm
keyword={keyword}
onChange={handleKeywordChange}
onFocus={setRecommendVisible}
onBlur={setRecommendInvisible}
/>
<SearchRecommend isVisible={isInputFocused} />
</div>
);
}
display: none
과 조건부 렌더링의 차이점은 DOM 트리에 반영되느냐이다.
전자는 눈에 보이지는 않을 뿐 DOM 트리에 있기 때문에 개발자 도구에서 볼 수 있지만 후자는 렌더링되었을 때만 DOM 트리에서 볼 수 있다.
그럼 어떤 방식이 더 좋을까하는 고민이 생겼고 나름 두 가지 방법에 대한 장단점을 생각해보았다.
그럼 검색창의 경우는 어떨까? 검색창의 목적을 생각하면 검색어를 입력하고 그에 대한 연관 검색어, 추천 검색어를 보는 것이 필연적이다. 그리고 focus 여부에 따라 결과창이 나타나고 없어지는 것이 빈번하기 때문에 조건부 렌더링보다는 display: none
이 더 적합할 것이라 생각한다.
display: none, visibility: hidden, opacity: 0 비교
display: none
: DOM 트리에 있지만 렌더링 X, 공간 차지 Xvisibility: hidden
: DOM 트리에 있고 렌더링 O, 공간 차지 O, 요소 뒷 부분 클릭 가능opacity: 0
: DOM 트리에 있고 렌더링 O, 공간 차지 O, 요소 뒷 부분 클릭 불가능
유명한 웹 사이트를 참고하는 것도 좋은 방법이라 네이버 검색창을 개발자 도구로 봤는데 display: none
으로 결과창을 안 보이게 하고 있었다. 나름대로 추측한 위와 같은 이유가 아닐까 생각하는데 결론은 상황에 맞게 적합한 방식을 사용하는 것이 맞다.
그럼 조건부 렌더링은 어떨 때 사용하면 좋을지도 고민해봤는데 탭 메뉴와 같이 state에 따라 노출되는 컨텐츠가 달라지고 서로 영향을 주지 않는 독립적인 컨텐츠라면 조건부 렌더링이 맞는 것 같다.