태그 기능과 마찬가지로 자동완성기능을 구현하기 위해서는 어떻게 해야할지 알아보기 위해 google창을 먼저 살펴봤습니다.
검색창에 input값을 입력하면 밑에 input값과 유사한 전체 단어을 dropdown 형식으로 보여주는 자동 완성 기능을 구현했습니다.
inputValue
input에 들어간 값
string.includes()
메서드 활용빈문자열 ''
isHaveInputValue
입력된 input값이 있는지의 여부
Boolean
형태false
dropDownList
dropdown에 보여줄 자동완성된 단어목록(리스트)
array.map()
메서드 활용[]
dropDownItemIndex
내가 선택한
자동완성된 단어 item의 index
dropDownList.map()
를 통해 dropdown이 나왔을 때, 해당하는 index
와 dropDownItemIndex
이 동일하면 배경색을 lightgrey로 변경해서 선택되었음을 알려줍니다.inputValue
, isHaveInputValue
값 업데이트했습니다.아래,위,enter
눌릴 때 dropdown의 요소를 선택하기 위해 키보드에 버튼을 눌릴 때마다, 아래의 조건에 따라 dropDownItemIndex
값 업데이트했습니다.조건
isHaveInputValue
이 없다면 dropdown의 영역도 없을 것이기 때문에 이러한 키보드 이벤트를 고려하지 않습니다.
dropDownItemIndex
dropDownList.length - 1 > dropDownItemIndex
~~dropDownItemIndex >= 0
dropDownItemIndex
선택): dropDownItemIndex >= 0
// 조건을 위와 같이 설정한 이유
dropDownList = ['apple', 'banana', 'javascript']
dropDownList.length // 3
beasts.indexOf('apple') // 0
beasts.indexOf('banana') // 1
beasts.indexOf('javascript') // 2
inputValue
이 존재할 때만 보여져야 하기 때문에 isHaveInputValue
이 true일 때 렌더링되도록 했습니다.dropDownList
state인데, 만약 하나도 없을 경우 빈 배열만 나오기 때문에 dropDownList.length === 0
일 때에는 해당하는 단어가 없다고 렌더링했습니다.map()
메서드를 통해 <DropDownItem>
컴포넌트를 반복적으로 만들었습니다.<DropDownItem>
을 눌렀을 때 (=클릭했을 때)inputValue
state를 업데이트 하고,isHaveInputValue
state를 false로 변경했습니다.<DropDownItem>
을 마우스오버했을 때dropDownItemIndex
state로 업데이트합니다.import React, { useEffect, useState } from 'react'
import styled from 'styled-components'
import Title from '../components/Title'
const wholeTextArray = [
'apple',
'banana',
'coding',
'javascript',
'원티드',
'프리온보딩',
'프론트엔드',
]
const AutoComplete = () => {
const [inputValue, setInputValue] = useState('')
const [isHaveInputValue, setIsHaveInputValue] = useState(false)
const [dropDownList, setDropDownList] = useState(wholeTextArray)
const [dropDownItemIndex, setDropDownItemIndex] = useState(-1)
const showDropDownList = () => {
if (inputValue === '') {
setIsHaveInputValue(false)
setDropDownList([])
} else {
const choosenTextList = wholeTextArray.filter(textItem =>
textItem.includes(inputValue)
)
setDropDownList(choosenTextList)
}
}
const changeInputValue = event => {
setInputValue(event.target.value)
setIsHaveInputValue(true)
}
const clickDropDownItem = clickedItem => {
setInputValue(clickedItem)
setIsHaveInputValue(false)
}
const handleDropDownKey = event => {
//input에 값이 있을때만 작동
if (isHaveInputValue) {
if (
event.key === 'ArrowDown' &&
dropDownList.length - 1 > dropDownItemIndex
) {
setDropDownItemIndex(dropDownItemIndex + 1)
}
if (event.key === 'ArrowUp' && dropDownItemIndex >= 0)
setDropDownItemIndex(dropDownItemIndex - 1)
if (event.key === 'Enter' && dropDownItemIndex >= 0) {
clickDropDownItem(dropDownList[dropDownItemIndex])
setDropDownItemIndex(-1)
}
}
}
useEffect(showDropDownList, [inputValue])
return (
<WholeBox>
<Title text='AutoComplete' />
<InputBox isHaveInputValue={isHaveInputValue}>
<Input
type='text'
value={inputValue}
onChange={changeInputValue}
onKeyUp={handleDropDownKey}
/>
<DeleteButton onClick={() => setInputValue('')}>×</DeleteButton>
</InputBox>
{isHaveInputValue && (
<DropDownBox>
{dropDownList.length === 0 && (
<DropDownItem>해당하는 단어가 없습니다</DropDownItem>
)}
{dropDownList.map((dropDownItem, dropDownIndex) => {
return (
<DropDownItem
key={dropDownIndex}
onClick={() => clickDropDownItem(dropDownItem)}
onMouseOver={() => setDropDownItemIndex(dropDownIndex)}
className={
dropDownItemIndex === dropDownIndex ? 'selected' : ''
}
>
{dropDownItem}
</DropDownItem>
)
})}
</DropDownBox>
)}
</WholeBox>
)
}
const activeBorderRadius = '16px 16px 0 0'
const inactiveBorderRadius = '16px 16px 16px 16px'
const WholeBox = styled.div`
padding: 10px;
`
const InputBox = styled.div`
display: flex;
flex-direction: row;
padding: 16px;
border: 1px solid rgba(0, 0, 0, 0.3);
border-radius: ${props =>
props.isHaveInputValue ? activeBorderRadius : inactiveBorderRadius};
z-index: 3;
&:focus-within {
box-shadow: 0 10px 10px rgb(0, 0, 0, 0.3);
}
`
const Input = styled.input`
flex: 1 0 0;
margin: 0;
padding: 0;
background-color: transparent;
border: none;
outline: none;
font-size: 16px;
`
const DeleteButton = styled.div`
cursor: pointer;
`
const DropDownBox = styled.ul`
display: block;
margin: 0 auto;
padding: 8px 0;
background-color: white;
border: 1px solid rgba(0, 0, 0, 0.3);
border-top: none;
border-radius: 0 0 16px 16px;
box-shadow: 0 10px 10px rgb(0, 0, 0, 0.3);
list-style-type: none;
z-index: 3;
`
const DropDownItem = styled.li`
padding: 0 16px;
&.selected {
background-color: lightgray;
}
`
export default AutoComplete