React Hook의 useState
를 이용해서 반응형 헤더와 메뉴 동작을 만들어보려고 한다.
useState
로 CSS
속성 값을 바꿔서 메뉴가 동작하는 것처럼 보일 수 있다.
App.js - import
import React, { useState } from "react";
import styled from "styled-components";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faApple } from "@fortawesome/free-brands-svg-icons";
import { faBars, faUser, faTimes } from "@fortawesome/free-solid-svg-icons";
아이콘을 쉽게 가져올 수 있는
Fontawesome
설치가 필요하다.
CSS
는styled-components
를 사용했다.
jsx
구조├── Header
│ ├── toggle Button
│ ├── logo
│ ├── user Button
│ ├── header__menulist
│ └── header__right
jsx
구조를 이렇게까지 작성한 이유는 반응형을 구현하기 위해서 부모, 자식 관계나 순서가 꽤나 중요했다.
아직CSS
가 미숙한 부분일수도 있지만, 위의 구조처럼 진행했을 때 좀 더 수월하게 반응형 헤더를 구현할 수 있었다.
웹 화면에서는 toggle Button
과 userButton
이 display:none;
으로 되어있다.
반응형이 적용되는 화면에서는 toggleButton
과 userButton
이 display:block;
으로 되고 header__menulist
와 header__right
가 display:none;
으로 된다.
이 상황에서 header__menulist
나 header__right
의 display
속성이 flex
로 바뀌게 되면서 width:100%
속성을 적용하면 자연스럽게 toggle Button
, logo
, userButton
아래로 내려갈 수 있다.
App.js - javascript
function App() {
const [isToggled, setIsToggled] = useState(false);
const [userToggled, setUserToggled] = useState(false);
return (
<Header isToggled={isToggled} userToggled={userToggled}>
{/* 햄버거 버튼(bar) */}
<div
className="toggle"
onClick={() => {
setIsToggled(!isToggled);
}}
>
<FontAwesomeIcon icon={!isToggled ? faBars : faTimes} />
</div>
{/* Apple 로고 */}
<div className="logo">
<FontAwesomeIcon icon={faApple} />
</div>
{/* User 버튼 */}
<div
className="user"
onClick={() => {
setUserToggled(!userToggled);
}}
>
<FontAwesomeIcon icon={!userToggled ? faUser : faTimes} />
</div>
{/* 메뉴 리스트 */}
<ul className="header__menulist">
<li>Mac</li>
<li>iPad</li>
<li>iPhone</li>
<li>Watch</li>
<li>Music</li>
<li>고객지원</li>
</ul>
{/* User 메뉴 리스트 */}
<ul className="header__right">
<li>Login</li>
<li>Register</li>
</ul>
</Header>
);
}
export default App;
App.js - CSS
const Header = styled.div`
max-width: 1280px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
background-color: black;
.logo {
margin: 0 1rem;
font-size: 2rem;
}
.header__menulist {
list-style: none;
display: flex;
}
.header__left {
display: flex;
}
.header__right {
list-style: none;
display: flex;
}
.header__right div {
margin: 0 1rem;
}
li {
padding: 0 1rem;
}
.toggle {
display: none;
font-size: 1.5rem;
padding: 1rem 1rem;
}
.user {
display: none;
font-size: 1.5rem;
padding: 1rem 1rem;
}
@media screen and (max-width: 768px) {
flex-wrap: wrap;
.header__right {
display: ${(props) => (props.userToggled ? "flex" : "none")};
flex-direction: column;
width: 100%;
background-color: black;
}
.header__menulist {
display: ${(props) => (props.isToggled ? "flex" : "none")};
flex-direction: column;
width: 100%;
background-color: black;
}
.header__menulist li,
.header__right li {
margin: 1rem 0;
padding: 0;
}
.toggle {
display: block;
}
.user {
display: block;
}
}
`;
jsx 부분
const [isToggled, setIsToggled] = useState(false);
const [userToggled, setUserToggled] = useState(false);
<div className="toggle"
onClick={() => {
setIsToggled(!isToggled);
}}
>
<Header isToggled={isToggled} userToggled={userToggled}>
CSS 부분
@media screen and (max-width: 768px) {
.header__right {
display: ${(props) => (props.userToggled ? "flex" : "none")};
flex-direction: column;
width: 100%;
background-color: black;
}
.header__menulist {
display: ${(props) => (props.isToggled ? "flex" : "none")};
flex-direction: column;
width: 100%;
background-color: black;
}
}
useState
를 통해서isToggled
와userToggled
의 초깃값을false
로 지정한다.
onClick event
가 실행됐을 때 해당 값을 반대로(false -> true
) 바꾼다.
해당 값이CSS
부분으로 전달되고display
속성에 영향을 미친다.
CSS
부분으로 전달되는 것은<Header isToggled={isToggled} userToggled={userToggled}>
이 부분을 통해서 props로 전달할 수 있다.
onClick
이벤트로 바뀐 값을 CSS
로 넘겨주는 부분에서 꽤나 많은 시간을 썼다.
onClick
이벤트로만 해당 기능이 작동하기 때문에 다른 영역을 선택한다고 display
속성이 바뀌지 않는다.