* React, Styled-Component, Storybook을 활용한 UI 컴포넌트 개발
* Styled Components를 활용해 ClickToEdit 커스텀 컴포넌트를 구현
ClickToEdit Component
완성 예시
ClickToEdit 컴포넌트는 input 창을 클릭하면 수정이 가능하고,
input 창이 아닌 다른 곳을 클릭하면 수정한 내용이 반영되는 기능을 가진 컴포넌트이다.
필요한 컴포넌트
InputView (div) : Tab 상위바에서 메뉴를 선택할 수 있는 구역 --
- label : '이름', '나이' l -----------------> clickToEdit 컴포넌트 (부모)
- MyInput : 실제로 이름, 나이 값을 입력할 수 있는 박스 ------
- InputBox ----------------------------------------------
- InputEdit(input) : 포커스가 되면, Edit이 가능한 input 박스가 된다. ㅣ --> MyInput 컴포넌트 (자식)
- span : 포커스를 잃으면 Edit가 불가능한 span 박스가 된다. ----------
- InputBox ----------------------------------------------
state
- MyInput 컴포넌트는 아래와 같은 state가 존재한다.
- isEditMode state는 boolean 값을 상태로 가진다.
- newValue state는 보이게 될 값을 상태로 가지며, isEditMode 가 true 일 때 업데이트된다.
- ClickToEdit 컴포넌트는 아래와 같은 state가 존재한다.
- name , age를 state로 가지며, 앞서 작성한 MyInput 컴포넌트를 활용해 state를 업데이트할 수 있다.
- ClickToEdit 컴포넌트가 자식으로 가지는 MyInput 컴포넌트에 handleValueChange 메서드를 props로 받아 value를 업데이트한다.
(상태 끌어올리기)
구현 코드
ClickToEdit UI Component 코드
import { useEffect, useState, 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]);
const handleClick = () => {
setEditMode(true);
};
const handleBlur = () => {
setEditMode(false);
handleValueChange(newValue);
};
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={(newValue) => setAge(newValue)} />
</InputView>
<InputView>
<div className='view'>이름 {name} 나이 {age}</div>
</InputView>
</>
);
};
포인트
- onFocus : 포커스를 받은 경우 이벤트 설정
- onBlur : 포커스가 해지될 때 이벤트 설정
input 창 안에 내용이 없을 때, 포커스가 안 되는 문제
- (문제 이유)
- input 창 안에 내용을 넣지 않고 onBlur 이벤트가 일어날 때,
- 문구가 담겨야 할 span 안에 문구가 없기 때문에 화면에서 사라짐
- (1번째 방법) MyInput 컴포넌트에서 값이 없을 경우, '클릭하여 편집' 문구가 표시되도록 함
- (2번째 방법) span에 display: inline-block 과 height: 100%, width: 100% 속성을 넣어줌.
// 예
export const InputBox = styled.div`
.
.
.
> span {
height: 100%;
width: 100%;
display: inline-block;
}
`;
input 창 안에 렌더링 되는 상태 -> newValue
제일 아래 div에 렌더링 되는 상태 -> name, age
- MyInput 컴포넌트에 화면에 렌더링 될 name, age 상태와 상태변경함수인 handleValueChange를 내려준 다음,
- InputEdit 컴포넌트에 onChange가 일어나면,
- newValue라는 새로운 상태에 변경되고 있는 값을 일단 담아주고
- newValue의 값은 span에 담긴다.
- InputEdit 컴포넌트에 onBlur가 일어나면,
- 그때서야 내려 받은 상태변경함수인 handleValueChange를 이용해 부모의 name, age 상태를 변경해준다.
728x90
'FE > React' 카테고리의 다른 글
Props Drilling이란? (0) | 2023.04.21 |
---|---|
[React] 전역 상태의 필요성 (0) | 2023.04.21 |
Styled Components로 Tag 만들기 (0) | 2023.04.20 |
Styled Components로 Tab 만들기 (0) | 2023.04.20 |
Styled Components로 Toggle 만들기 (0) | 2023.04.20 |