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",
};