React
ClickToEdit
- 값을 입력했을 때에 정해진 요소의 값을 입력한 값으로 바꿔주는 기능 구현
컴포넌트 짜기
- 입력할 컴포넌트와 입력을 출력할 컴포넌트로 나눈다.
- 나머지는
styled-component로 감싸준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
return (
<>
<InputView>
<MyInput />
</InputView>
<InputView>
<MyInput />
</InputView>
<InputView>
<MyInput />
</InputView>
</>
);
Styled-Components
- InputBox, InputEdit, InputView 컴포넌트로 하위 요소들을 정렬해준다.
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
export const InputBox = Styled.div`
text-align: center;
display: inline-block;
width: 150px;
height: 30px;
border: 1px #bbb dashed;
border-radius: 10px;
margin-left: 1rem;
`;
export const InputEdit = styled.div`
text-align: center;
display: inline-block;
width: 150px;
height: 30px;
`;
export const InputView = styled.div`
text-align: center;
align-items: center;
margin-top: 3rem;
div.view {
margin-top: 3rem;
}
`;
기능 구현
상태값 작성
useRef로 요소를 선택한다.isEditMode로 값을 수정할 수 있는 상태를 만든다.newValue로 입력될 값을 지정한다.useEffect를 사용하여isEditMode,value의 상태값이 변할 때마다 re-render 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
const inputEl = useRef(null);
const [isEditMode, setEditMode] = useState(false);
const [newValue, setNewValue] = useState(value);
useEffect(() => {
if (isEditMode) {
inputEl.current.focus();
}
}, [isEditMode]);
useEffect(() => {
setNewValue(value);
}, [value]);
컴포넌트에 이벤트 핸들러 부여
handleClick함수로 입력하고 있는 상태를 알려주기 위해setEditMode를true와false로 바꿔주는 역할을 한다.handleBlur함수로 마우스의 선택이 벗어났을 때 발생시키며, 마우스가 벗어난 상태를 저장하고setEditMode(false)입력한 값을newValue의 상태로 바꾼다.handleInputChange함수로input에서 입력한value값을newValue에 저장한다.
1
2
3
4
5
6
7
8
9
10
11
12
const handleClick = () => {
setEditMode(!isEditMode);
};
const handleBlur = () => {
setEditMode(false);
handleValueChange(newValue);
};
const handleInputChange = (e) => {
setNewValue(e.target.value);
};
코드
- InputBox 컴포넌트는
div를 갖고, InputEdit 컴포넌트는input요소를, InputView 컴포넌트는div를 갖는다. - ClickToEdit 컴포넌트를 먼저 보면, ClickToEdit 컴포넌트는 InputView와 MyInput 두 가지 컴포넌트를 갖고 있다.
- InputView 컴포넌트는
div요소를 갖고 MyInput 컴포넌트는value값과handleValueChange함수를 인자로 받는다. - MyInput 컴포넌트 안의
handleValueChange함수는e를 인자로 받는데, 이 인자는 ClickToEdit 컴포넌트에서 인자로 받은newValue => setName(newValue)이다. handleValueChange함수는setNewValue를 통해 MyInput 컴포넌트의newValue값을 ClickToEdit 컴포넌트의 MyInput으로 전달받은 입력값으로 상태를 변경한다.useEffect를 통해 re-render한다.- ClickToEdit 컴포넌트의 MyInput 컴포넌트로 전달받은
value인자는, 기존의name값을 뜻하는데, MyInput 컴포넌트의newValue는name을 초기값으로 갖고 있다. - 이제 MyInput 컴포넌트를 들여다보면
isEditMode가 거짓일 때,span태그를 보여주고newValue를 갖고 있다.span태그의newValue는 초기값인, ClickToEdit 컴포넌트로부터 받은value인name이다.
span태그를 클릭 시handleClick함수를 실행시킨다.handleClick함수는setEditMode를 통해isEditMode를true값으로 바꾸는데,useEffect에 의해isEditMode가 참이기 때문에inputEl로 설정해 준input태그를focus해준다.
isEditMode가true값으로 바뀌면return문을 통해 InputEdit 컴포넌트를 갖게 된다.- InputEdit 컴포넌트는
input태그를 갖고 있으며,input태그 안에 값을 입력할 때마다onChange={handleInputChange}가 실행되어,handleInputChange함수가newValue상태값을setNewValue를 통해 event를 발생시킨 현재의target의value값으로 바꿔준다. input태그가 아닌 다른 곳을 클릭할 시, focus가 풀리면서 이벤트 핸들러인onBlur가 실행되고handleBlur함수를 실행시킨다.handleBlur에 의해isEditMode의 상태는false로 바뀌고 ClickToEdit 컴포넌트의handleValueChange를 역으로 실행시켜 우리가 입력한 값을newValue의 값으로 갖게 한다.isEditMode의 상태가false로 바뀌었기 때문에 다시span태그를 생성하여 입력할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
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
import { useState, useEffect, useRef } from "react";
import styled from "styled-components";
export const InputBox = styled.div`
text-align: center;
display: inline-block;
width: 150px;
height: 30px;
border: 1px #bbb dashed;
border-radius: 10px;
margin-left: 1rem;
`;
export const InputEdit = styled.input`
text-align: center;
display: inline-block;
width: 150px;
height: 30px;
`;
export const InputView = styled.div`
text-align: center;
align-items: center;
margin-top: 3rem;
div.view {
margin-top: 3rem;
}
`;
export const MyInput = ({ value, handleValueChange }) => {
const inputEl = useRef(null);
const [isEditMode, setEditMode] = useState(false);
const [newValue, setNewValue] = useState(value);
useEffect(() => {
if (isEditMode) {
inputEl.current.focus();
}
}, [isEditMode]);
useEffect(() => {
setNewValue(value);
}, [value]);
const handleClick = () => {
setEditMode(!isEditMode);
};
const handleBlur = () => {
setEditMode(false);
};
const handleInputChange = (e) => {
setNewValue(e.target.value);
};
return (
<InputBox>
{isEditMode ? (
<InputEdit
type="text"
value={newValue}
ref={inputEl}
onBlur={handleBlur}
onChange={handleInputChange}
/>
) : (
<span onClick={handleClick}>{newValue}</span>
)}
</InputBox>
);
};
const cache = {
name: "김코딩",
age: 20,
};
export const ClickToEdit = () => {
const [name, setName] = useState(cache.name);
const [age, setAge] = useState(cache.age);
return (
<>
<InputView>
<label>이름</label>
<MyInput
value={name}
handleValueChange={(newValue) => setName(newValue)}
/>
</InputView>
<InputView>
<label>나이</label>
<MyInput value={age} handleValueChange={(newAge) => setName(newAge)} />
</InputView>
<InputView>
<div className="view">
이름{name}나이{age}
</div>
</InputView>
</>
);
};
stories 작성
- 컴포넌트명과
stories.js를 작성하면 컴포넌트의stories로 인식한다. - Tag 컴포넌트를 불러온다.
- 기본값의
title은Example의 폴더 안에 ClickToEdit 컴포넌트를 의미한다. - 기본값의
component는 ClickToEdit 컴포넌트를 지칭한다. stories는 storybook- stories 설명보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from "react";
import { ClickToEdit } from "./components/ClickToEdit.js";
export default {
title: "Example/ClickToEdit",
component: ClickToEdit,
};
const Template = (args) => <ClickToEdit {...args} />;
export const Primary = Template.bind({});
Primary.args = {
primary: true,
label: "ClickToEdit",
};