React
Autocomplete
- 갖고 있는 정보에 일치하는 문자를 입력했을 경우, 아래에 문자와 일치하는 정보를 나타내는 기능 구현
컴포넌트 짜기
- 입력창을 갖고 있는 컴포넌트와 입력을 받았을 때 하위에 자동완성 기능을 나타낼 컴포넌트로 나눈다.
- 컴포넌트 안에 요소를 작성한다.
1
2
3
4
5
6
return (
<InputContainer>
<input />
</InputContainer>
<DropDown />
)
Styled-Components
- 기존의 정보값을 갖고 있을 배열을 임의로 작성해준다.
- InputContainer에
div
를 만들어 속성을 정한다.focus-within
은input
을 클릭했을 때 발생
- 상태값에 따라 클래스를 추가하고 빼내어 속성값을 부여한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
const autoValue = [
"apple",
"angle",
"adios",
"banana",
"bare",
"bread",
"candy",
"car",
"drop",
"down",
"function",
];
const boxShadow = "0 4px 6px rgb(32 33 36 / 28%)";
const inactiveBorderRadius = "1rem 1rem 1rem 1rem";
export const InputContainer = styled.div`
margin-top: 8rem;
background-color: #ffffff;
display: flex;
felx-direction: row;
padding: 1rem;
border: 1px solid rgb(223, 225, 229);
border-radius: ${inactiveBorderRadius};
z-index: 3;
box-shadow: 0;
&:focus-within {
box-shadow: ${boxShadow};
}
> input {
flex: 1 0 0;
background-color: transparent;
border: none;
margin: 0;
padding: 0;
outline: none;
font-size: 16px;
}
> div.delete-button {
cursor: pointer;
}
`;
export const DropDownContainer = styled.ul`
background-color: #ffffff;
display: block;
margin-left: auto;
margin-right: auto;
list-styled-type: none;
margin-block-start: 0;
margin-block-end: 0;
margin-inline-start: 0;
margin-inline-end: 0;
padding-inline-start: 0p;
margin-top: -1px;
padding: 0.5rem 0;
border: 1px solid rgb(223, 225, 229);
border-radius: 0 0 1rem 1rem;
box-shadow: ${boxShadow}
z-index: 3;
> li {
padding: 0 1rem;
}
> li:hover {
background-color: rgb(223,225,229);
}
&.select {
background-color: rgb(223,225,229);
}
`;
기능 구현
상태값 작성
- 초기값은 기존에 작성된
autoValue
배열을 갖는다. hasText
는input
창에 값이 입력되었는지를 확인한다.inputValue
는 배열에input
에 입력된 값을 추가하기 위해,input
에 작성된value
를 의미한다.options
는 초기에 작성한autoValue
값을 의미한다.selectedOption
는 우리가 선택한 옵션에 따른index
를 가르킨다.useEffect
를 사용하여input
에 입력한 값이 빈 배열이 아닐 때, 기존에 갖고있는 배열과 중복되지 않는다면 값을 추가하기 위해inputValue
의 상태값이 변경될 때마다 새롭게 렌더링한다.push
를 이용하면 기존에 갖고있는 배열의 주소값이 변하지 않아 리액트에서 상태의 변화를 알아차리지 못 하기 때문에filter
,mpa
,...
을 사용하여 추가한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
const [hasText, setHasText] = useState(false);
const [inputValue, setInputValue] = useState("");
const [options, setOptions] = useState(autoValue);
const [selectedOption, setSelectedOption] = useState(0);
useEffect(() => {
if (inputValue === "") {
setHasText(false);
}
if (inputValue !== "") {
setOptions(autoValue.filter((option) => option.includes(inputValue)));
}
}, [inputValue]);
컴포넌트에 이벤트 핸들러 부여
- 값을 입력하거나, 자동완성을 선택하거나, 삭제버튼을 눌렀을 때에 발생할 이벤트를 작성한다.
handleInputChange
함수는 이벤트를 발생시킨 요소의 값을 그대로 받아 기존의 배열에 추가하는 역할을 하며,setHasText
상태를true
로 만들어input
값의 유무를 설정한다.handleDropDownClick
함수는 우리가 선택한 자동완성된 값을input
요소의value
로 넣어주는 역할을 한다.handleDeleteButtonClick
함수는 입력한input
창의value
값을 공백으로 만들어, 입력한 값을 삭제해주는 역할을 한다.handleKeyUp
함수는input
을 작성한 상태에서 자동완성된 값을 선택해 주기 위해 실행되며,ArrowDown
은 화살표 아래 키를 의미하며,ArrowUp
은 화살표 위 키를 눌렀을 때에index
에 값을 추가하거나 빼서index
값을 설정해준다.- 작성 후
Enter
를 누른다면handleDropDown
함수로 전달인자를 넘겨 기존 배열의index
를 선택한다. - 선택 후에는
setSelectedOption
을 0으로 만들어, 0번째index
값을 갖게 한다.
- 작성 후
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const handleInputChange = (event) => {
setInputValue(event.target.value);
setHasText(true);
};
const handleDropDownClick = (clickedOption) => {
setInputValue(clickedOption);
};
const handleDeleteButtonClick = () => {
setInputValue("");
};
const handleKeyUp = (event) => {
if (event.key === "ArrowDown" && selectedOption < options.length - 1) {
setSelectedOption(selectedOption + 1);
}
if (event.key === "ArrowUp" && selectedOption > 0) {
setSelectedOption(selectedOption - 1);
}
if (event.key === "Enter") {
handleDropDownClick(options[selectedOption]);
setSelectedOption(0);
}
};
코드
- 적절한 요소에 이벤트 핸들러를 부여한다.
input
요소에 입력된 값을 뜻하는value
는inputValue
인 상태값이다.input
에 값이 입력되면handleInputChange
함수가 실행되는데, 실행되는 함수는input
에 입력된value
값으로 상태값이 변한다.- 함수의 실행 형태로 전달하면 안된다.
- 키가 입력됐을 때, 키를 판별하여 자동완성의
index
를 구하기 위해selectedOption
값이 변경된다.
delete-button
을 누를 때,handleDelelteButtonClick
함수를 실행시키는데, 입력된 값을 초기화한다.- 자동완성 기능을 가진 DropDown 컴포넌트는 DropDownContainer 컴포넌트를 갖고 있으며, 기존 값을 갖고 있는 배열
options
, 선택한index
의 값에 클래스를 부여할selectedOption
을 인자로 넘겨준다..- DropDownContainer 컴포넌트는
li
요소에options
의 각각의 값과index
를 갖고,option
을handleComboBox
가 가진handleDropDownClick
함수로 전달인자를 넘겨주어 선택한option
요소를input
창에 넣어준다.
- DropDownContainer 컴포넌트는
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import {useState,useEffect} from "react"
import styled from "styled-components"
const autoValue = [
"apple",
"angle",
"adios",
"banana",
"bare",
"bread",
"candy",
"car",
"drop",
"down",
"function"
]
const boxShadow = "0 4px 6px rgb(32 33 36 / 28%)";
const inactiveBorderRadius = "1rem 1rem 1rem 1rem";
export const InputContainer = styled.div`
margin-top: 8rem;
background-color: #ffffff;
display: flex;
felx-direction: row;
padding: 1rem;
border: 1px solid rgb(223, 225, 229);
border-radius: ${inactiveBorderRadius};
z-index: 3;
box-shadow: 0;
&:focus-within {
box-shadow: ${boxShadow};
}
> input {
flex: 1 0 0;
background-color: transparent;
border: none;
margin: 0;
padding: 0;
outline: none;
font-size: 16px;
}
> div.delete-button {
cursor: pointer;
}
`
export const DropDownContainer = styled.ul`
background-color: #ffffff;
display: block;
margin-left: auto;
margin-right: auto;
list-styled-type: none;
margin-block-start: 0;
margin-block-end: 0;
margin-inline-start: 0;
margin-inline-end: 0;
padding-inline-start: 0p;
margin-top: -1px;
padding: 0.5rem 0;
border: 1px solid rgb(223, 225, 229);
border-radius: 0 0 1rem 1rem;
box-shadow: ${boxShadow}
z-index: 3;
> li {
padding: 0 1rem;
}
> li:hover {
background-color: rgb(223,225,229);
}
&.select {
background-color: rgb(223,225,229);
}
`
export const Autoncomplete = () => {
const [hasText, setHasText] = useState(false);
const [inputValue, setInputValue] = useState("");
const [options, setOptions] = useState(autoValue)
const [selectedOption, setSelectedOption] = useState(0)
useEffect(()=>{
if(inputValue === ""){
setHasText(false);
}
if(inputValue !== ""){
setOptions(
autoValue.filter((option)=>option.includes(inputValue))
)
}
},[inputValue])
const handleInputChange = (event) => {
setInputValue(event.target.value);
setHasText(true);
}
const handleDropDownClick = (clickedOption) => {
setInputValue(clickedOption);
}
const handleDeleteButtonClick = () => {
setInputValue("")
}
const handleKeyUp = (event) => {
if(event.key === "ArrowDown" && selectedOption < options.length - 1){
setSelectedOption(selectedOption + 1);
}
if(event.key === "ArrowUp" && selectedOption > 0){
setSelectedOption(selectedOption - 1);
}
if(event.key === "Enter"){
handleDropDownClick(options[selectedOption])
setSelectedOption(0);
}
}
return (
<div>
<InputContainer>
<input type="text"
value={inputValue}
onChange={(event)=>{
handleInputChange(event);
}}
onKeyUp={(event)=>{
handleKeyUp(event)
}}/>
<div className="delete-button" onClick={handleDeleteButtonClick} />
</InputContainer>
{options.length && hasText ? (
<DropDown
options={options
handleComboBox={handleDropDownClick}
selectedOption={selectedOption}
}
/>) : null}
</div>
)
}
export const DropDown = ({options, handleComboBox, selectedOption})=>{
return (
<DropDownContainer>
{options.map(option,index)=>{
return (
<li
key={index}
onClick={()=>handleComboBox(option)}
className={selectedOption === index ? "select" : ""}
>
{option}
</li>
)
}}
</DropDownContainer>
)
}
stories 작성
- 컴포넌트명과
stories.js
를 작성하면 컴포넌트의stories
로 인식한다. - Tag 컴포넌트를 불러온다.
- 기본값의
title
은Example
의 폴더 안에 Tag 컴포넌트를 의미한다. - 기본값의
component
는 Tag 컴포넌트를 지칭한다. stories
는 storybook- stories 설명보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from "react";
import { Tag } from "./components/Tag.js";
export default {
title: "Example/Tag",
component: Tag,
};
const Template = (args) => <Tag {...args} />;
export const Primary = Template.bind({});
Primary.args = {
primary: true,
label: "Tag",
};
stories 작성
- 컴포넌트명과
stories.js
를 작성하면 컴포넌트의stories
로 인식한다. - Autocomplete 컴포넌트를 불러온다.
- 기본값의
title
은Example
의 폴더 안에 Autocomplete 컴포넌트를 의미한다. - 기본값의
component
는 Autocomplete 컴포넌트를 지칭한다. stories
는 storybook- stories 설명보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from "react";
import { Autocomplete } from "./components/Autocomplete.js";
export default {
title: "Example/Autocomplete",
component: Autocomplete,
};
const Template = (args) => <Autocomplete {...args} />;
export const Primary = Template.bind({});
Primary.args = {
primary: true,
label: "Autocomplete",
};