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