React
Tag
- 값을 입력 후
Enter
키를 누르면 값이 태그 형태로 들어가는 기능 구현
컴포넌트 짜기
- 컴포넌트를 나눈다.
- 우리가 작성해야 하는 것은 태그를 감싸고 있는 박스 하나.
- 그 안에 태그를 달아주는 것과 입력하는 것을 모두 작성했지만 각 컴포넌트로 쪼갤 수 있다.
ul
안에li
가 있고,li
안에span
요소를 넣어, 이를 입력하는 배열만큼 각li
와span
안에 값을 뿌려 출력한다.
1
2
3
4
5
6
7
8
9
10
return (
<TagContainer>
<ul>
<li>
<span></span>
</li>
</ul>
<input />
</TagContainer>
);
Styled-Components
- TagContainer 컴포넌트를 정렬한다.
ul
,li
,span
,input
의style
을 정해준다.- TagesContainer 컴포넌트는 중앙에 위치하며, 그 안에 오는 요소들은 컴포넌트의 시작인 좌측부터 차곡차곡 쌓인다.
- 만약 컴포넌트의 넓이값을 넘어가면 아래로 떨어뜨려 정렬한다.
li
는tags
클래스를 갖고 있다.- 넓이값은 우리가 입력하는 텍스트를 기준으로 자동으로 지정한다.
li
안에 있는 텍스트는 상화좌우 자동정렬한다.li
의list-style
을 없앤다.
span
은tags-close-icon
클래스를 갖고 있다.input
은flex
비율 1값을 갖고 있다.input
요소를 클릭하여focus
되면 외곽선이 투명해진다.input
이focus-withon
이 되었을 때 외곽선이 생기는 것을 색상을 변경해준다.
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
import {useState} from "react"
import styled from "styled-components"
export const TagContainer.div`
margin: 8rem auto;
display: flex;
align-items: flex-start;
flex-wrap: wrap;
min-height: 48px;
width: 480px;
padding: 0 8px;
border: 1px solid rgb(214, 216, 218);
border-radius: 6px;
> ul {
display: flex;
flex-wrap: wrap;
padding: 0;
margin: 8px 0 0;
> .tag {
width: auto;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
padding: 0 8px;
font-size: 14px;
list-style: none;
border-radius: var(--coz-purple-600);
> .tag-close-icon {
display: block;
width: 16px;
height: 16px;
line-height: 16px;
text-align: center;
font-size: 14px;
margin-left: 8px;
color: var(--coz-purbple-600);
border-radius: 50%;
background: #fff;
cursor: pointer;
}
}
}
> input {
flex: 1;
border: none;
height: 46px;
font-size: 14px;
padding: 4px 0 0 0;
:focus {
outline: transparent;
}
}
&:focus-within {
border: 1px solid var(--coz-purple-600);
}
`
기능 구현
상태값 작성
- 초기값은 배열
["choi","kim"]
을 갖고 있다. - 태그를 작성하고
Enter
키를 눌렀을 때 함수를 실행한다.- 만약 기존값과 내가 이벤트가 발생한
event.target
의 입력값인value
와 겹치는 값이 없다면, - 입력한
value
값이 공백이 아니라면, - 기존 배열에 내가 작성한
event.target.value
를 넣어준다.
- 만약 기존값과 내가 이벤트가 발생한
close icon
이 부여된span
을 눌렀을 때에 함수를 실행시킨다.- 작성된 기존값의
index
와 내가 클릭한 값의index
를 일치시켜, 일치하는 값을 제외한 배열을 재구성한다.
- 작성된 기존값의
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const allTags = ["choi", "kim"];
const [tags, setTags] = useState(allTags);
const removeTags = (indexToRemove) => {
setTags(tags.filter((tag) => tag !== tags[indexToRemove]));
};
const addTags = (event) => {
if (
event.key === "Enter" &&
!tags.includes(event.target.value) &&
event.target.value !== ""
) {
setTags([...tags, event.target.value]);
event.target.value = "";
}
};
컴포넌트에 이벤트 핸들러 부여
li
요소 안span
에 우리가 배열이 가진 값을 부여해 출력한다.map()
의 전달인자index
는 배열의index
를 말하며span className="tags-close-icon"
을 눌렀을 때,removeTags
를 실행시킨다.removeTags
에map()
으로 넘겨받은index
를tag.filter
에 넘겨주어 내가 클릭한, 이미 작성된 값의index
를filter
로 거른 새로운 배열이 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
return (
<>
<TagContaier>
<ul>
{tags.map((tag, index)=> {
<li key={index} className="tags">
<span className="tag-title">{tags}</span>
<span className="tags-close-icon" onClick={()=>removeTags(index)}>
</span>
})}
</ul>
<input className="tag-input" type="text"
onClick={(event)=>{
addTags(event)
}} placeholder="Press enter to add tags"><>
</TagContaier>
</>
)
코드
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
import {useState} from "react"
import styled from "styled-components"
export const TagContainer.div`
margin: 8rem auto;
display: flex;
align-items: flex-start;
flex-wrap: wrap;
min-height: 48px;
width: 480px;
padding: 0 8px;
border: 1px solid rgb(214, 216, 218);
border-radius: 6px;
> ul {
display: flex;
flex-wrap: wrap;
padding: 0;
margin: 8px 0 0;
> .tag {
width: auto;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
padding: 0 8px;
font-size: 14px;
list-style: none;
border-radius: var(--coz-purple-600);
> .tag-close-icon {
display: block;
width: 16px;
height: 16px;
line-height: 16px;
text-align: center;
font-size: 14px;
margin-left: 8px;
color: var(--coz-purbple-600);
border-radius: 50%;
background: #fff;
cursor: pointer;
}
}
}
> input {
flex: 1;
border: none;
height: 46px;
font-size: 14px;
padding: 4px 0 0 0;
:focus {
outline: transparent;
}
}
&:focus-within {
border: 1px solid var(--coz-purple-600);
}
`
export const Tag = () => {
const allTags = ["choi","kim"]
const [tags,setTags] = useState(allTags)
const removeTags = (indexToRemove) => {
setTags(tags.filter((tag)=> tag !== tags[indexToRemove]))
}
const addTags = (event) => {
if(
event.key === "Enter" &&
!tags.includes(event.target.value) &&
event.target.value !== ""
){
setTags([...tags, event.target.value]);
event.target.value = "";
}
}
return (
<>
<TagContaier>
<ul>
{tags.map((tag, index)=> {
<li key={index} className="tags">
<span className="tag-title">{tags}</span>
<span className="tags-close-icon" onClick={()=>removeTags(index)}>
</span>
})}
</ul>
<input className="tag-input" type="text"
onClick={(event)=>{
addTags(event)
}} placeholder="Press enter to add tags" />
</TagContaier>
</>
)
}
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",
};