Modal
import { useState, useRef } from "react";
import styled from "styled-components";
export const Modal = () => {
const [isOpen, setIsOpen] = useState(false);
const refBackdrop = useRef(null);
const openModalHandler = (event) => {
event.preventDefault();
setIsOpen(!isOpen);
};
return (
<>
<ModalContainer>
<ModalBtn onClick={openModalHandler}>
{isOpen ? "Opened!" : "Open Modal"}
</ModalBtn>
{isOpen ? (
<ModalBackdrop
ref={refBackdrop}
onClick={(e) => {
if (e.target === refBackdrop.current) openModalHandler(e);
}}
>
<ModalView>
<p>Hello World!</p>
</ModalView>
</ModalBackdrop>
) : undefined}
</ModalContainer>
</>
);
};
기능 구현
- 필요한 state
- 창이 떠있는지 아닌지를 판단하는 불리언 값을 가진 상태
- 삼항연산자를 이용해 state 값에 따라 모달창을 보여주거나 숨김
- 모달의 배경을 클릭했을 때 모달창이 꺼지는 것을 구현하기 위해서 버튼에 적용한 이벤트(
onClick
)를 배경에도 동일하게 작성- 이때, 이벤트 버블링(부모요소에 적용된 이벤트가 자식에게도 적용되는 현상)을 막기 위한 방법은 여러가지가 있다.
useRef
를 사용해 배경의 DOM 주소에 접근하여 판별 (비추천)<ModalBackdrop ref={refBackdrop} onClick={(e) => { if (e.target === refBackdrop.current) openModalHandler(e); } } >
- HTML에서 사용되는 접근성을 위한 속성인
role
을 이용해 event.target에 접근하여 판별export const ModalView = styled.div.attrs((props) => ({ role: "dialog", }) // 중략 <ModalBackdrop onClick={(e) => { if (e.target.role !== 'dialog') openModalHandler(e); } } >
- 이벤트 버블링을 막기 위해 기본적으로 제공되는 이벤트 객체 메서드
stopPropagation()
를 모달창에 적용<ModalBackdrop onClick={(e) => openModalHandler(e)}} > <ModalView onClick={(e) => e.stopPropagation()}>
- 이때, 이벤트 버블링(부모요소에 적용된 이벤트가 자식에게도 적용되는 현상)을 막기 위한 방법은 여러가지가 있다.
Tab
export const Tab = () => {
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>
{menuArr.map((el, index) => {
return (
<li
key={el.name}
className={currentTab === index ? "submenu focused" : "submenu"}
onClick={() => selectMenuHandler(index)}
>
{el.name}
</li>
);
})}
</TabMenu>
<Desc>
<p>{menuArr[currentTab].content}</p>
</Desc>
</div>
</>
);
};
기능 구현
- 필요한 state
- 현재 탭을 확인하고 렌더 할 넘버 값(index)을 가진 변수
- 탭으로 표시 해야 하는 내용들을 배열 형태로 작성하거나 받아오고,
.map
을 이용해 탭 메뉴를 출력
- 클릭 이벤트 핸들러에는 해당
li
의 index를 받아 state를 변경하는 함수 작성
- 현재 탭의 상태를 표시 할 클래스 스타일을 작성하고 state와 렌더된 index를 비교하여 클래스를 추가/제거하는 삼항연산자 작성
Tag
export const Tag = () => {
const initialTags = ["CodeStates", "kimcoding"];
const [tags, setTags] = useState(initialTags);
const removeTags = (indexToRemove) => {
setTags((prev) => prev.filter((el, index) => index !== indexToRemove));
};
const addTags = (event) => {
setTags((prev) => prev.concat(event));
};
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);
}}
>
×
</span>
</li>
))}
</ul>
<input
className="tag-input"
type="text"
onKeyUp={(e) => {
if (
e.key === "Enter" &&
e.target.value !== "" &&
!tags.includes(e.target.value)
) {
addTags(e.target.value);
e.target.value = "";
console.log("실행");
}
}}
placeholder="Press enter to add tags"
/>
</TagsInput>
</>
);
};
기능 구현
- 필요한 state
- 현재 태그를 표시 하여 스트링 값을 배열의 형태로 저장하는 상태
- 태그 추가는 입력창의 value를 받아서 state에 추가하는 함수를 작성하고 키 업 이벤트를 통해 상태 변경 함수를 실행 → 이 때, 입력창에 value가 없거나 이미 존재하는 태그 일 경우를 조건을 통해 동작하지 않게 할 수 있음
- 태그 제거는
.map
으로 렌더된li
의 index를 인자로 받아 state 내 값을 제거하는 함수를 작성
Toggle
import { useState } from 'react';
import styled from 'styled-components';
export const Toggle = () => {
const [isOn, setisOn] = useState(false);
const toggleHandler = () => {
setisOn(!isOn);
};
return (
<>
<ToggleContainer onClick={toggleHandler}
// TODO : 클릭하면 토글이 켜진 상태(isOn)를 boolean 타입으로 변경하는 메소드가 실행되어야 합니다.
>
<div className={isOn ? 'toggle-container toggle--checked' : 'toggle-container'}/>
<div className={isOn ? 'toggle-circle toggle--checked' : 'toggle-circle'}/>
</ToggleContainer>
<Desc>
{isOn ? 'Toggle Switch ON' : 'Toggle Switch OFF'}
</Desc>
</>
);
};
기능 구현
- 필요한 state
- 토글의 현재 상태를 판단하는 불리언 값을 가진 상태
- 클릭을 하면 상태를 변경하는 함수 작성 후 이벤트 핸들러로 연결
- 토글의 상태에 따라 스타일이 변할 수 있게 삼항연산자로 클래스명 제어
- 추가되는 클래스에
transition
속성을 부여 해 자연스러운 동작 구현 가능
- 추가되는 클래스에
Autocomplete
export const Autocomplete = () => {
const [hasText, setHasText] = useState(false);
const [inputValue, setInputValue] = useState("");
const [options, setOptions] = useState([]);
const [opIndex, setOpIndex] = useState(-1);
useEffect(() => {
if (inputValue === "") {
setHasText(false);
} else {
setHasText(true);
setOptions(deselectedOptions.filter(el => el.indexOf(inputValue) >= 0))
}
}, [inputValue]);
const handleInputChange = (event) => {
setInputValue(event);
};
const handleDropDownClick = (clickedOption) => {
setInputValue(clickedOption);
};
const handleDeleteButtonClick = () => {
setInputValue("");
};
const handleKeyUp = (event) => {
if(event === 'ArrowUp') setOpIndex(prev => {
if(prev >= 0) return prev - 1;
else return prev;
})
else if(event === 'ArrowDown') setOpIndex(prev => {
if(prev < options.length - 1) return prev + 1;
else return prev;
})
else if(event === 'Enter') {
handleDropDownClick(options[opIndex]);
setOpIndex(prev => prev = -1);
}
}
return (
<div className="autocomplete-wrapper">
<InputContainer hasText={hasText}>
<input
type="text"
value={inputValue}
onChange={(event) => handleInputChange(event.target.value)}
onKeyUp={(event) => handleKeyUp(event.code)}
/>
<div className="delete-button" onClick={handleDeleteButtonClick}>
×
</div>
</InputContainer>
{hasText ? <DropDown options={options} opIndex={opIndex} handleComboBox={handleDropDownClick}/> : undefined}
</div>
);
};
export const DropDown = ({ options, opIndex, handleComboBox }) => {
return (
<DropDownContainer>
{options.map((el, index) => (
<li className={opIndex === index ? 'active' : ''} key={index} onClick={() => handleComboBox(el)} >{el}</li>
))}
</DropDownContainer>
);
};
기능 구현
- 필요한 state
hasText
: input 내에 값이 존재하는지 판단하는 불리언 값의 상태
inputValue
: input의 value 값을 확인하고 저장하는 스트링 값의 상태
options
: 자동완성 추천 값을 배열의 형태로 가진 상태
opIndex
: (Advanced) 자동 완성 추천 값을 키보드로 조작하기 위한 index 값을 가진 상태
- 자동완성 컴포넌트 자체는
hasText
를 이용해 삼항연산자를 활용해 렌더하고, 내부에는options
를.map
을 이용해 렌더- 해당 요소를 클릭했을 때 그 값이 input에 들어갈 수 있도록 클릭 이벤트 핸들러 작성
- 자동 완성은
inputValue
가 변경될 때 자동완성의 원본 데이터를filter
를 이용해options
를 변경
- 방향키를 이용한 제어는 키 업 이벤트를 이용해 각각의 방향키에
opIndex
상태를 변경하는 기능과 엔터를 입력하면 해당 요소가 input에 들어갈 수 있도록inputValue
를 변경하는 함수를 작성해 연결하고, 선택 된 추천 요소를 볼 수 있게 클래스에 스타일을 작성하여 index에 맞춰서 클래스 변경.
Uploaded by N2T