1. 만들고 싶은 기능 정리하기
auto completing은 검색기능을 만들 때 필수적으로 구현해야 하는 기능입니다.
알파벳을 하나 이상 input에 입력하면 그 입력값으로 시작하는 추천 검색어가 랜더링돼 user에게 보여지는 것이 핵심입니다.
- 입력 알파벳이 하나 이상이면 suggestion list 랜더링하기
- 입력 알파벳이 하나 이상이면 clear button이 보이도록 하기
- suggestion을 클릭하면 그 값을 input value로 세팅하기
- input에 알파벳을 입력 후 suggestion을 클릭하지 않은 상태에서, input과 suggestion 외부에서 클릭이벤트가 발생했을 때
4-1. suggestion list가 닫히도록 하기
4-2. input값은 입력값 그대로 남아 있게 하기 - clear button 클릭시 input값 비우기, suggestion list 닫기
여기에서는 세부적으로 5개 기능을 구현했습니다.
2. Auto complete Component
컴포넌트 내에서 상태관리가 필요한 것은 input의 text value와 suggestion값, 그리고 search가 시작됐는지 확인하기 위한 값 3가지입니다.
const [suggestions, setSuggestions] = useState([]);
const [text, setText] = useState("");
const [isAutoCompleting, setIsAutoCompleting] = useState(false);
2-1. 입력 알파벳이 하나 이상이면 suggestion list 렌더링하기
컴포넌트가 처음 랜더링될 때 나올 element들을 작성해줍니다.
return (
<div
className={styles.autoCompleteBox}
>
<input
value={text}
type="text"
onChange={handleTextChanged}
onClick={() => setIsAutoCompleting(true)}
/>
{suggestions.length > 0 && renderSuggestions()}
</div>
);
input에 값이 들어오면 onChange 함수를 이용해 textvalue를 세팅해주고, 이 값을 이용해 data에 들어있는 값들과 같은 것들을 찾아주면 됩니다.
빈 배열의 list를 만들고 textvalue값으로 data에 있는 값들을 조회해 list에 넣어줍니다. suggestions의 length가 1 이상일 경우 랜더링이 일어나도록 했으므로 이 list배열을 suggestion에 세팅해주면 됩니다.
const handleTextChanged = e => {
let suggestedDataList = [];
let textValue = e.target.value;
if (textValue !== "") {
const regex = new RegExp(`^${tempValue}`, "i");
suggestedDataList = initData
.filter(e => regex.test(e.label))
}
setSuggestions(suggestedDataList);
setText(textValue);
};
만약 data에 같은 알파벳으로 시작하는 추천검색어가 많을 경우, 알파벳 순서대로 정렬해주기 위해 sort method를 이용해주면 됩니다.
const items = [
{ name: 'Edward', value: 21 },
{ name: 'Sharpe', value: 37 },
{ name: 'And', value: 45 },
{ name: 'The', value: -12 },
{ name: 'Magnetic', value: 13 },
{ name: 'Zeros', value: 37 }
];
// value 기준으로 정렬
items.sort(function (a, b) {
if (a.value > b.value) {
return 1;
}
if (a.value < b.value) {
return -1;
}
// a must be equal to b
return 0;
});
// name 기준으로 정렬
items.sort(function(a, b) {
let nameA = a.name.toUpperCase(); // ignore upper and lowercase
let nameB = b.name.toUpperCase(); // ignore upper and lowercase
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
// 이름이 같을 경우
return 0;
});
MDN 예제를 코드에 적용해줍니다.
const compareLabel = (a, b) => {
const labelA = a.label.toUpperCase();
const labelB = b.label.toUpperCase();
if (labelA < labelB) return -1;
if (labelA > labelB) return 1;
return 0;
};
if (textValue !== "") {
const regex = new RegExp(`^${tempValue}`, "i");
suggestedDataList = initData
.filter(e => regex.test(e.label))
.sort(compareLabel)
}
2.2 Clear button
입력값과 추천검색어 랜더링을 초기화할 버튼을 만들어줍니다.
const clear = () => {
setIsAutoCompleting(false);
setText("");
setSuggestions([]);
};
{isAutoCompleting &&
<button
onClick={clear}
>
x
</button>}
2.3 Suggestion값을 input값으로 세팅하기
알파벳을 입력하면 추천검색어가 나오기 시작합니다. 원하는 검색어를 마우스로 클릭하면 sugggestion list는 감추고 input의 textContent로 세팅되도록 했습니다.
const selectSuggestion = value => {
setSuggestions([]);
setText(value);
}
<ul data-testid="test-suggestionlist">
{suggestions.map((item, index) =>
<li
key={index}
onClick={() => selectSuggestion(item.label)}
>
{item.label}
</li>
)}
</ul>
3. 예외처리
suggestion 선택이전, 외부에서 클릭이벤트가 발생했을 때 검색을 중단하도록 해야 합니다.
input에 알파벳을 입력 후 suggestion을 클릭하지 않은 상태에서, input과 suggestion 외부에서 클릭이벤트가 발생했을 때를 위한 예외처리가 필요했습니다. 검색입력어는 input에 그대로 남아 있지만 suggestion list는 닫히도록 해야합니다.
input과 suggestion 외부에서 이벤트가 발생하므로, 그 둘을 감싸고 있는 부모 태그에 ref를 걸어두고 클릭 이벤트가 발생하는 곳이 부모 태그 외부인지 확인합니다. 검색어 search가 시작됐는지를 확인하는 상태변수를 false로 바꿔 search를 중단하도록 하면 됩니다.
const handleClick = e => {
if (boxRef.current && !boxRef.current.contains(e.target)) {
setIsAutoCompleting(false);
}
}
return (
<div className={styles.autoCompleteBox} ref={boxRef}
)
'React > Example' 카테고리의 다른 글
React - Input 값을 Tag로 만들기 (0) | 2023.04.16 |
---|---|
React - Modal만들기(이벤트 버블링 차단) (0) | 2023.04.16 |