export const Modal = () => {
//모달창이 열리고 닫힌 상태
const [isOpen, setIsOpen] = useState(false);
//모달 버튼을 클릭하면 상태를 변경한다
const openModalHandler = () => {
setIsOpen(!isOpen);
};
return (
<>
<ModalContainer>
<ModalBtn onClick={openModalHandler}>
{isOpen ? "Opened!" : "Open Modal"}
</ModalBtn>
{/* 모달이 열린 상태면 ? 배경, 모달창이 열린다 : null */}
{isOpen ? (
<>
<ModalBackdrop onClick={openModalHandler}>
<ModalView>
<Btn onClick={openModalHandler}>X</Btn>
<h2>Hello World</h2>
</ModalView>
</ModalBackdrop>
</>
) : null}
</ModalContainer>
</>
);
};
모달 배경(모달창이 오픈 되었을 때의 배경)을 클릭하면 창이 닫히도록 onClick 이벤트를 주었다.
export const ModalBackdrop = styled.div`
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
`;
모달 배경은 화면에 꽉 차도록 position: fixed를 설정해주었다.
const Desc = styled.div`
text-align: center;
margin: 10px;
`;
export const Toggle = () => {
//토글의 on, off 상태
const [isOn, setisOn] = useState(false);
const toggleHandler = () => {
setisOn(!isOn);
};
return (
<>
<ToggleContainer onClick={toggleHandler}
{/* 토글 ON이면 toggle--checked라는 클래스를 추가해주었다. */}
<div className={`toggle-container ${isOn ? "toggle--checked" : ""}`} />
<div className={`toggle-circle ${isOn ? "toggle--checked" : ""}`} />
</ToggleContainer>
<Desc>Toggle switch {isOn ? "ON" : "OFF"}</Desc>
</>
);
};
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;
}
> .toggle-container.toggle--checked {
background-color: #5055de;
}
클래스가 입혀졌을 때의 스타일은 > .class명.추가class명
과 같이 작성한다.
export const Tab = () => {
//클릭시 변경되는 currentTab의 상태 (index)
const [currentTab, setCurrentTab] = 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) => {
setCurrentTab(index)
};
return (
<>
<div>
<TabMenu>
{/* 클릭한 탭은 focused 클래스가 추가된다*/}
{menuArr.map((e, i) => (
<li
key={i}
onClick={() => selectMenuHandler(i)}
className={`${currentTab === i ? "submenu focused" : "submenu"}`}
>
{menuArr[i].name}
</li>
))}
</TabMenu>
<Desc>
<h2>{menuArr[currentTab].content}</h2>
</Desc>
</div>
</>
);
};
array.map()
으로 생성한 메뉴 탭 li
태그에 onClick 이벤트를 주어서
클릭한 li
태그는 해당 탭의 index를 이벤트 핸들러로 넘겨주고
클릭한 index로 상태를 변경한다.
export const Tag = () => {
const initialTags = ['초기', '태그'];
//태그 배열 상태
const [tags, setTags] = useState(initialTags);
//input의 onChange 이벤트 상태
const [value, setValue] = useState();
//태그 삭제는 filter 함수로 구현했다
const removeTags = (indexToRemove) => {
setTags(tags.filter((e, i) => i !== indexToRemove))
};
const addTags = (event) => {
// - 이미 입력되어 있는 태그인지 검사하여 이미 있는 태그라면 추가하지 말기
// - 아무것도 입력하지 않은 채 Enter 키 입력시 메소드 실행하지 말기
// - 태그가 추가되면 input 창 비우기
if(event.key === "Enter" && !tags.includes(value) && value ) {
setTags([...tags, value]);
setValue();
}
else if (event.key === 'Enter' && !value) {
setValue();
}
}
const onChangehandler = (e) => {
setValue(e.target.value);
}
return (
<>
<TagsInput>
<ul id='tags'>
{tags.map((tag, index) => (
<li key={index} className='tag'>
<span className='tag-title'>{tag}</span>
<span onClick={() => removeTags(index)} className='tag-close-icon'>
x
</span>
</li>
))}
</ul>
<input
className='tag-input'
type='text'
onKeyUp={(event) => addTags(event)}
placeholder='Press enter to add tags'
onChange={onChangehandler}
value={value || ''}
/>
</TagsInput>
</>
);
};
input의 value를 value={value}로 설정해주니
Warning: A component is changing an uncontrolled input of type undefined to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
콘솔창에 위와 같은 에러가 나와서 undefined
를 다루기 위해 value || ''
로 설정해주었다.