블로깅 주제
- React Custom Component
1. 지금 현재, 당신의 기분이나 느낌을 표현해 주세요.
- 역시 금요일... 꼭 금요일에 나오는 과제들은 다 어렵더라?ㅠㅠ
2. 오늘 무엇을 학습한 내용 중 지금 떠올릴 수 있는 단어를 모두 나열해 주세요.
- React Custom Component 만들기
3. 2에서 작성한 단어를 가지고, 오늘의 학습 내용을 설명해 보세요.
- 과제 : React Custom Component
Modal.js
Modal UI 컴포넌트는 기존의 브라우저 페이지 위에 새로운 윈도우 창이 아닌, 레이어를 까는 것을 말한다.
import { useState } from 'react';
import styled from 'styled-components';
// 컨테이너
export const ModalContainer = styled.div`
// TODO : Modal을 구현하는데 전체적으로 필요한 CSS를 구현합니다.
display : flex;
justify-content: center;
align-items: center;
// 높이를 정렬해줘야 완전히 가운데로 옴
height : 100%;
`;
// 모달 클릭시 까맣게 되는 배경
export const ModalBackdrop = styled.div`
// TODO : Modal이 떴을 때의 배경을 깔아주는 CSS를 구현합니다.
// 위치를 고정시킨다. fixed는 보이는 곳에서 고정, absolute는 전체에서 고정인 느낌
position: fixed;
display: flex;
justify-content: center;
align-items: center;
// 스크롤을 어디에 두던 위치를 정해줌. 전체를 채우고싶을 때 자주 사용!
top: 0;
left: 0;
right: 0;
bottom: 0;
// 아무색도 주지않고(까만색이 됨) 반투명도를 준다.
background-color: rgba(0, 0, 0, 0.5);
`;
// 모달 열고 닫는 버튼
export const ModalBtn = styled.button`
background-color: var(--coz-purple-600);
text-decoration: none;
border: none;
padding: 20px;
color: white;
border-radius: 30px;
cursor: grab;
`;
// 버튼 클릭시 나오는 모달 창
export const ModalView = styled.div.attrs(props => ({
// attrs 메소드를 이용해서 아래와 같이 div 엘리먼트에 속성을 추가할 수 있습니다.
role: 'dialog'
}))`
// TODO : Modal창 CSS를 구현합니다.
border-radius: 10px;
background-color: #ffffff;
width: 300px;
height: 100px;
margin-bottom : 300px;
display : flex;
flex-direction: column;
align-items : center;
//첫번째 요소의 바로 아래 자식인 노드를 선택
> div.desc {
font-size : 15px;
color : #475ed4;
}
`;
export const Exitbtn = styled(ModalBtn)`
background-color: #ffffff;
color: black;
margin : 10px;
padding: 5px 10px;
`;
export const Modal = () => {
const [isOpen, setIsOpen] = useState(false);
const openModalHandler = () => {
// TODO : isOpen의 상태를 변경하는 메소드를 구현합니다.
setIsOpen(!isOpen);
};
return (
<>
<ModalContainer>
<ModalBtn
// TODO : 클릭하면 Modal이 열린 상태(isOpen)를 boolean 타입으로 변경하는 메소드가 실행되어야 합니다.
onClick = {openModalHandler}
>
{isOpen? "Opened!" :"Open Modal"}
{/* TODO : 조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때는 ModalBtn의 내부 텍스트가 'Opened!' 로 Modal이 닫힌 상태(isOpen이 false인 상태)일 때는 ModalBtn 의 내부 텍스트가 'Open Modal'이 되도록 구현해야 합니다. */}
</ModalBtn>
{/* TODO : 조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때만 모달창과 배경이 뜰 수 있게 구현해야 합니다. */}
{isOpen ? <ModalBackdrop onClick={openModalHandler}>
// 부모 요소는 이벤트 실행안되도록. '이벤트 버블링'이라는 현상을 막아줌.
<ModalView onClick={(event) => {event.stopPropagation()}}>
// x 버튼
<Exitbtn onClick={openModalHandler}>×</Exitbtn>
<div className='desc'>HELLO CODESTATES!</div>
</ModalView>
</ModalBackdrop>
: null}
</ModalContainer>
</>
);
};
※ stopPropagation() -> 이벤트 버블링 방지
자식 컴포넌트(여기서는 x 버튼)에 이벤트 핸들러가 걸려있을 때 부모 컴포넌트(여기서는 모달 뷰)에도 같은 핸들러가 작동이 되고, 이것을 이벤트가 점차 위로 퍼지는 이벤트 버블링 현상이라고 한다.
이때 부모 컴포넌트에서는 클릭 시 모달이 꺼지는 행위가 작동 하지 않게 만드려면 모달 뷰의 이벤트 핸들러에 stopPropagation()를 활용해주면 된다. 위의 코드에서 onClick={(event) => {event.stopPropagation()} 를 추가해주면 모달창이 떴을 때 배경을 클릭하면 모달창이 꺼지지만, 모달창 내부를 클릭하면 아무일도 일어나지 않는다.
※ CSS - position
요소의 위치를 지정하기 위한 속성으로 5가지(static, relative, absolute, fixed, sticky) 타입이 있다.
어떤 위치를 기준점으로 잡는지는 position 속성의 각 타입 마다 차이가 있다. 위치 이동의 속성값 적용 시 top, bottom 중 하나, left, right 중 하나만 적용시킬수 있음에 유의한다.
* sticky는 지원하지 않는 브라우저가 다수 있다.
https://tobegood.tistory.com/entry/position
+ position: fixed + top,left,right,bottom: 0 => 화면 전체를 채우고 싶을 때 자주 쓴다.
Toggle.js
Toggle UI 컴포넌트는 두 가지 상태만을 가지고 있는 스위치이다. 예를 들어, 한번 누르면 불이 들어오고 한번 누르면 불이 나가는 스위치를 Toggle Switch 라고 할 수 있다.
import { useState } from 'react';
import styled from 'styled-components';
const ToggleContainer = styled.div`
position: relative;
margin-top: 8rem;
left: 47%;
cursor: pointer;
> .toggle-container {
width: 50px;
height: 24px;
border-radius: 30px;
background-color: #8b8b8b;
transition : 0.2s;
// TODO : .toggle--checked 클래스가 활성화 되었을 경우의 CSS를 구현합니다.
// & 옆에 띄어쓰기 있으면 작동안됨.
&.toggle--checked{
background-color: var(--coz-purple-600);
transition : 0.2s;
}
}
> .toggle-circle {
position: absolute;
top: 1px;
left: 1px;
width: 22px;
height: 22px;
border-radius: 50%;
background-color: #ffffff;
// TODO : .toggle--checked 클래스가 활성화 되었을 경우의 CSS를 구현합니다.
&.toggle--checked{
left: 27px;
transition : 0.2s;
}
}
`;
const Desc = styled.div`
// TODO : 설명 부분의 CSS를 구현합니다.
position: relative;
top: 5px;
left: 44%;
`;
export const Toggle = () => {
const [isOn, setisOn] = useState(false);
const toggleHandler = () => {
// TODO : isOn의 상태를 변경하는 메소드를 구현합니다.
setisOn(!isOn);
};
return (
<>
<ToggleContainer
// TODO : 클릭하면 토글이 켜진 상태(isOn)를 boolean 타입으로 변경하는 메소드가 실행되어야 합니다.
onClick={toggleHandler}
>
{/* TODO : 아래에 div 엘리먼트 2개가 있습니다. 각각의 클래스를 'toggle-container', 'toggle-circle' 로 지정하세요. */}
{/* TIP : Toggle Switch가 ON인 상태일 경우에만 toggle--checked 클래스를 div 엘리먼트 2개에 모두 추가합니다. 조건부 스타일링을 활용하세요. */}
<div className={`toggle-container ${isOn ? "toggle--checked" : ""}`} />
<div className={`toggle-circle ${isOn ? "toggle--checked" : ""}`} />
</ToggleContainer>
{/* TODO : Desc 컴포넌트를 활용해야 합니다. */}
{/* TIP: Toggle Switch가 ON인 상태일 경우에 Desc 컴포넌트 내부의 텍스트를 'Toggle Switch ON'으로, 그렇지 않은 경우 'Toggle Switch OFF'가 됩니다. 조건부 렌더링을 활용하세요. */}
<Desc>{isOn? "Toggle Switch ON" :"Toggle Switch OFF"}</Desc>
</>
);
};
Tab.js
Tab UI 컴포넌트는 동일한 메뉴 라인에서 뷰를 전환할 때 사용한다.
import { useState } from 'react';
import styled from 'styled-components';
// TODO: Styled-Component 라이브러리를 활용해 TabMenu 와 Desc 컴포넌트의 CSS를 구현합니다.
const TabMenu = styled.ul`
background-color: #dcdcdc;
color: rgba(73, 73, 73, 0.5);
font-weight: bold;
display: flex;
flex-direction: row;
justify-items: center;
align-items: center;
list-style: none;
margin-bottom: 7rem;
.submenu {
${'' /* 기본 Tabmenu 에 대한 CSS를 구현합니다. */}
background-color: #dcdcdc;
width: calc(100% / 3);
height: 40px;
text-align: center;
transition: 0.5s;
padding: 10px;
}
.focused {
${'' /* 선택된 Tabmenu 에만 적용되는 CSS를 구현합니다. */}
background-color: var(--coz-purple-600);
color: white;
width: calc(100% / 3);
height: 40px;
text-align: center;
transition: 0.5s;
}
// & div.desc {
// text-align: center;
// }
`;
const Desc = styled.div`
text-align: center;
`;
export const Tab = () => {
// TIP: Tab Menu 중 현재 어떤 Tab이 선택되어 있는지 확인하기 위한
// currentTab 상태와 currentTab을 갱신하는 함수가 존재해야 하고, 초기값은 0 입니다.
const [focusedTab,setFocusedTab] = useState(0);
const menuArr = [
{ name: 'Tab1', content: 'Tab menu ONE' },
{ name: 'Tab2', content: 'Tab menu TWO' },
{ name: 'Tab3', content: 'Tab menu THREE' },
];
const selectMenuHandler = (index) => {
// TIP: parameter로 현재 선택한 인덱스 값을 전달해야 하며, 이벤트 객체(event)는 쓰지 않습니다
// TODO : 해당 함수가 실행되면 현재 선택된 Tab Menu 가 갱신되도록 함수를 완성하세요.
setFocusedTab(index);
};
return (
<>
<div>
<TabMenu>
{/*TODO: 아래 하드코딩된 내용 대신에, map을 이용한 반복으로 코드를 수정합니다.*/}
{/*TIP: li 엘리먼트의 class명의 경우 선택된 tab 은 'submenu focused' 가 되며,
나머지 2개의 tab은 'submenu' 가 됩니다.*/}
{/* 핸들러 함수 연결 해줄 때 함수 실행 형태로 넣으면 안됨! 콜백함수 형태로 넣어야함!
selectMenuHandler(index) 이렇게만 넣으면 실행 형태가 됨*/}
{menuArr.map((el,index) => {
return <li key={index} className={`submenu${focusedTab === index ? " focused" : ""}`}
onClick = {() => selectMenuHandler(index)}>{el.name}</li>
})}
</TabMenu>
<Desc>
{/*TODO: 아래 하드코딩된 내용 대신에, 현재 선택된 메뉴 따른 content를 표시하세요*/}
<p>{menuArr[focusedTab].content}</p>
</Desc>
</div>
</>
);
};
Tag.js
Tag UI 컴포넌트는 레이블 지정을 통해 구성이나 분류에 도움이 되는 키워드 집합을 만들 때 자주 사용된다.
import { useState } from 'react';
import styled from 'styled-components';
// TODO: Styled-Component 라이브러리를 활용해 여러분만의 tag 를 자유롭게 꾸며 보세요!
export const TagsInput = styled.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 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: 6px;
margin: 0 8px 8px 0;
background: 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-purple-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 initialTags = ['CodeStates', 'kimcoding'];
const [tags, setTags] = useState(initialTags);
const removeTags = (indexToRemove) => {
// TODO : 태그를 삭제하는 메소드를 완성하세요.
let newTags = tags.slice();
newTags.splice(indexToRemove,1);
setTags(newTags);
};
const addTags = (event) => {
// TODO : tags 배열에 새로운 태그를 추가하는 메소드를 완성하세요.
// 이 메소드는 태그 추가 외에도 아래 3 가지 기능을 수행할 수 있어야 합니다.
// - 이미 입력되어 있는 태그인지 검사하여 이미 있는 태그라면 추가하지 말기
// - 아무것도 입력하지 않은 채 Enter 키 입력시 메소드 실행하지 말기
// - 태그가 추가되면 input 창 비우기
let newTag = event.target.value.trim();
if(!tags.includes(newTag) && newTag.length !== 0 && event.key === "Enter"){
let newTags = tags.slice();
newTags.push(newTag);
setTags(newTags);
event.target.value = '';
}
};
return (
<>
<TagsInput>
<ul id="tags">
{tags.map((tag, index) => (
<li key={index} className="tag">
<span className="tag-title">{tag}</span>
<span className="tag-close-icon" onClick={()=>removeTags(index)}>
{/* TODO : tag-close-icon이 tag-title 오른쪽에 x 로 표시되도록 하고,
삭제 아이콘을 click 했을 때 removeTags 메소드가 실행되어야 합니다. */}
x
</span>
</li>
))}
</ul>
<input
className="tag-input"
type="text"
onKeyUp={addTags}
placeholder="Press enter to add tags"
/>
</TagsInput>
</>
);
};
event.key를 사용하면 입력된 키의 값을 받을 수 있다.
addTags 함수안에 보면 input창에 입력된 값을 trim 해주고 있는데, 이는 공백만 입력한 경우에도 입력이 되지 않게 해주기 위함이다.
예를들어, let str = ' Hello '와 같은 str이 있다고 가정해보자. 이것을 str.trim() 해주게되면 양쪽의 빈칸이 다 사라지고 'Hello'만 남게해준다. 그렇기 때문에 tag 입력창에 ' '를 입력해주려고 하면 trim이 되서 value는 ''이 되고, 빈 값은 입력이 안되게 구현했기 때문에 결국 ' '도 입력이 안되게 된다.
Advanced Challenge
Autocomplete.js
Autocomplete UI 컴포넌트는 구글의 첫 화면과 같이 검색 폼처럼, 텍스트 input에 값을 입력하면, dropdown으로 input 값과 유사한 추천 검색 옵션을 보여주는 자동 완성 기능이다. 사용자 경험을 향상시킬 수 있다.
import { useState, useEffect } from 'react';
import styled from 'styled-components';
const deselectedOptions = [
'rustic',
'antique',
'vinyl',
'vintage',
'refurbished',
'신품',
'빈티지',
'중고A급',
'중고B급',
'골동품'
];
/* TODO : 아래 CSS를 자유롭게 수정하세요. */
const boxShadow = '0 4px 6px rgb(32 33 36 / 28%)';
const activeBorderRadius = '1rem 1rem 0 0';
const inactiveBorderRadius = '1rem 1rem 1rem 1rem';
export const InputContainer = styled.div`
margin-top: 8rem;
background-color: #ffffff;
display: flex;
flex-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-style-type: none;
margin-block-start: 0;
margin-block-end: 0;
margin-inline-start: 0px;
margin-inline-end: 0px;
padding-inline-start: 0px;
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;
// key 업다운으로 선택되었을 때 색 바뀜
&.selected{
background-color: violet;
}
// 마우스 올리면 색 바뀜
&:hover{
background-color: purple;
}
}
`;
export const Autocomplete = () => {
/**
* Autocomplete 컴포넌트는 아래 3가지 state가 존재합니다. 필요에 따라서 state를 더 만들 수도 있습니다.
* - hasText state는 input값의 유무를 확인할 수 있습니다.
* - inputValue state는 input값의 상태를 확인할 수 있습니다.
* - options state는 input값을 포함하는 autocomplete 추천 항목 리스트를 확인할 수 있습니다.
*/
const [hasText, setHasText] = useState(false);
const [inputValue, setInputValue] = useState('');
const [options, setOptions] = useState(deselectedOptions);
// 아무것도 선택되지 않았을 때 => -1
// options 처음은 0. 밑으로 내려갈 수록 1씩 증가
const [selected, setSelected] = useState(-1);
// useEffect를 아래와 같이 활용할 수도 있습니다.
useEffect(() => {
if (inputValue === '') {
setHasText(false);
}
}, [inputValue]);
// TODO : input과 dropdown 상태 관리를 위한 handler가 있어야 합니다.
const handleInputChange = (event) => {
/**
* handleInputChange 함수는
* - input값 변경 시 발생되는 change 이벤트 핸들러입니다.
* - input값과 상태를 연결시킬 수 있게 controlled component로 만들 수 있고
* - autocomplete 추천 항목이 dropdown으로 시시각각 변화되어 보여질 수 있도록 상태를 변경합니다.
*
* handleInputChange 함수를 완성하여 아래 3가지 기능을 구현합니다.
*
* onChange 이벤트 발생 시
* 1. input값 상태인 inputValue가 적절하게 변경되어야 합니다.
* 2. input값 유무 상태인 hasText가 적절하게 변경되어야 합니다.
* 3. autocomplete 추천 항목인 options의 상태가 적절하게 변경되어야 합니다.
* Tip : options의 상태에 따라 dropdown으로 보여지는 항목이 달라집니다.
*/
let inputText = event.target.value;
setInputValue(inputText);
setHasText(true);
setOptions(deselectedOptions.filter(el => el.includes(inputText)));
};
const handleDropDownClick = (clickedOption) => {
/**
* handleDropDownClick 함수는
* - autocomplete 추천 항목을 클릭할 때 발생되는 click 이벤트 핸들러입니다.
* - dropdown에 제시된 항목을 눌렀을 때, input값이 해당 항목의 값으로 변경되는 기능을 수행합니다.
*
* handleInputChange 함수를 완성하여 아래 기능을 구현합니다.
*
* onClick 이벤트 발생 시
* 1. input값 상태인 inputValue가 적절하게 변경되어야 합니다.
* 2. autocomplete 추천 항목인 options의 상태가 적절하게 변경되어야 합니다.
*/
setInputValue(clickedOption);
setOptions(deselectedOptions.filter(el => el.includes(clickedOption)));
};
const handleDeleteButtonClick = () => {
/**
* handleDeleteButtonClick 함수는
* - input의 오른쪽에 있는 X버튼 클릭 시 발생되는 click 이벤트 핸들러입니다.
* - 함수 작성을 완료하여 input값을 한 번에 삭제하는 기능을 구현합니다.
*
* handleDeleteButtonClick 함수를 완성하여 아래 기능을 구현합니다.
*
* onClick 이벤트 발생 시
* 1. input값 상태인 inputValue가 빈 문자열이 되어야 합니다.
*/
setInputValue('');
};
// Advanced Challenge: 상하 화살표 키 입력 시 dropdown 항목을 선택하고, Enter 키 입력 시 input값을 선택된 dropdown 항목의 값으로 변경하는 handleKeyUp 함수를 만들고,
// 적절한 컴포넌트에 onKeyUp 핸들러를 할당합니다. state가 추가로 필요한지 고민하고, 필요 시 state를 추가하여 제작하세요.
const handleKeyUp = (event) => {
if (event.key === 'ArrowDown' && selected < options.length - 1){
setSelected(selected+1);
}
if (event.key === 'ArrowUp' && selected > -1){
setSelected(selected-1);
}
if (event.key === 'Enter'){
handleDropDownClick(options[selected]);
setSelected(-1);
}
}
return (
<div className='autocomplete-wrapper'>
<InputContainer>
<input onChange={handleInputChange} value={inputValue} onKeyUp={handleKeyUp}></input>
{/* TODO : input 엘리먼트를 작성하고 input값(value)을 state와 연결합니다. handleInputChange 함수와 input값 변경 시 호출될 수 있게 연결합니다. */}
{/* TODO : 아래 div.delete-button 버튼을 누르면 input 값이 삭제되어 dropdown이 없어지는 handler 함수를 작성합니다. */}
<div onClick={handleDeleteButtonClick} className='delete-button'>×</div>
</InputContainer>
{/* TODO : input 값이 없으면 dropdown이 보이지 않아야 합니다. 조건부 렌더링을 이용해서 구현하세요. */}
{hasText ? <DropDown options={options} handleComboBox={handleDropDownClick} selected={selected}/> : null}
</div>
);
};
export const DropDown = ({ options, handleComboBox, selected }) => {
return (
<DropDownContainer>
{/* TODO : input 값에 맞는 autocomplete 선택 옵션이 보여지는 역할을 합니다. */}
{options.map((el,index) => <li key={index} className={selected===index? "selected" : ""}
onClick={()=>handleComboBox(el)}>{el}</li>)}
</DropDownContainer>
);
};
ClickToEdit.js
ClickToEdit 컴포넌트는 input 창을 클릭하면 수정이 가능하고, input 창이 아닌 다른 곳을 클릭하면 수정한 내용이 반영되는 기능을 가진 컴포넌트이다.
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]);
useEffect(() => {
setNewValue(value);
}, [value]);
const handleClick = () => {
// TODO : isEditMode 상태를 변경합니다.
setEditMode(true);
};
const handleBlur = () => {
// TODO : Edit가 불가능한 상태로 변경합니다.
handleValueChange(newValue);
setEditMode(false);
};
const handleInputChange = (e) => {
// TODO : 저장된 value를 업데이트합니다.
setNewValue(e.target.value);
};
return (
<InputBox>
{isEditMode ? (
<InputEdit
type='text'
value={newValue}
ref={inputEl}
onBlur={handleBlur}
onChange={handleInputChange}
// TODO : 포커스를 잃으면 Edit가 불가능한 상태로 변경되는 메소드가 실행되어야 합니다.
// TODO : 변경 사항이 감지되면 저장된 value를 업데이트 되는 메소드가 실행되어야 합니다.
/>
) : (
<span onClick={handleClick}
// TODO : 클릭하면 Edit가 가능한 상태로 변경되어야 합니다.
>{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>
</>
);
};
'코드스테이츠 SEB FE 41기 > Section 별 내용 정리' 카테고리의 다른 글
section3/Unit4/ [React] 상태 관리(11/2) (0) | 2022.11.02 |
---|---|
section3/Unit4/ [React] 상태 관리(11/1) (0) | 2022.11.01 |
section3/Unit3/ [React] Custom Component(10/27) (0) | 2022.10.27 |
section3/Unit2/ [사용자 친화 웹] UI/UX(10/26) (0) | 2022.10.26 |
section3/Unit2/ [사용자 친화 웹] UI/UX(10/25) (0) | 2022.10.25 |