사실 처음 리액트를 접했을때 썼어야 하는건데, 시간이 없어서 쓸 정신도 없었습니다.
팀 프로젝트에서 만든 내비게이션을 예시로 다시 천천히 써봅니다.
공식 문서 설명글(봐도 무슨말을 하는건지 잘 이해는 안감)
리액트의 대표적인 훅이고, 제일 처음 배우는 훅이라고 생각됩니다. 여기서는 함수 기반 상태 관리를 기준으로 작성했습니다.
내비게이션 바에서는 아래와 같은 기능을 구현할 때 사용됐습니다.
1.제품카테고리 버튼을 누르면 하단 서브메뉴가 나옴
2. 하단 카테고리 메뉴 데이터를 불러옴
Nav라는 폴더 내에 컴포넌트들이 많이 있습니다.
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Header } from './Header';
import { faBars, faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons';
import { NavDepth2 } from './NavDepth2.js';
import './Nav.scss';
const Nav = () => {
let [clicked, setClicked] = useState(false);
function handleCategory(e) {
!clicked ? setClicked(true) : setClicked(false);
}
return (
<nav className="nav-wrap">
<div className="content-wrap">
<Header />
<div className="nav">
<ul className="nav-depth1">
<li className="nav-category" onClick={handleCategory}>
<FontAwesomeIcon icon={faBars} className="menu-bar" />
<span>제품카테고리</span>
</li>
<li>
<Link to="/products/new">신상품</Link>
</li>
<li>
<Link to="/products/best">베스트</Link>
</li>
<li>
<Link to="/products/best">봄,들이기</Link>
</li>
<li>
<Link to="/products/best">위코뷰 스타일링</Link>
</li>
<li>
<Link to="/products/best">기획전</Link>
</li>
</ul>
<div className="search-section">
<input type="text" className="search-input" />
<button className="search-btn">
<FontAwesomeIcon icon={faMagnifyingGlass} />
</button>
</div>
</div>
</div>
<NavDepth2 show={clicked} />
</nav>
);
};
export default Nav;
깁니다.
일단 훅을 쓰려면 제일 해야 하는 것은 import로 훅을 끌어와야 한다는 겁니다.
//1. 리액트 훅 사용
import React, { useState, useEffect } from 'react';
//2. 리액트 라우터 돔에서 Link 컴포넌트 사용
import { Link } from 'react-router-dom';
//3. fontawesome 아이콘을 사용하기 위해 끌어온 것들
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBars, faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons';
//커스텀 컴포넌트 header(로고 있는 쪽)
import { Header } from './Header';
//커스텀 컴포넌트 NavDepth2(제품 카테고리 누르면 나오는 부분)
import { NavDepth2 } from './NavDepth2.js';
//css 연결
import './Nav.scss';
제품 카테고리 메뉴를 누르면 NavDepth2
컴포넌트가 생겼다 없어지는 거니까, 제품 카테고리 버튼에 onClick
이벤트를 걸어줍니다.
<li className="nav-category" onClick={handleCategory}>
<FontAwesomeIcon icon={faBars} className="menu-bar" />
<span>제품카테고리</span>
</li>
그럼 저 handleCategory
함수는 어떤 역할이냐면..
function handleCategory(e) {
!clicked ? setClicked(true) : setClicked(false);
}
매개변수로 이벤트를 (통칭 e
)받지만 사실 이 함수에서 이벤트는 딱히 하는게 없습니다.지워야겠네요.
저것을 풀어 쓰면 이렇게 됩니다.
if(!clicked){
setClicked(true);
}else{
setClicked(false);
}
clicked
의 결과가 거짓이라면,setClicked
라는 함수에 true 혹은 false 값을 준다는 아주 단순한 조건...그럼 그 setClicked
랑 clicked
는 뭐하는애들인데?
let [clicked, setClicked] = useState(false);
생소한 문법이 하나 나왔습니다.useState의 기본 양식입니다.
먼저 배열 하나에는 1개의 변수와 1개의 함수가 들어갑니다.
1.
clicked
는, 상태를 저장하는 변수 (즉let clicked;
)
2.setClicked
는, 상태를 갱신해주는 함수function setClicked(){ (무언가의 과정을 거쳐 clicked의 상태가 갱신되는 코드) return clicked }
useState
훅이 실행됩니다. 소괄호 안에 들어가는 부분은 초기값으로, 즉clicked
의 초기 상태는 false라는 이야기입니다.let clicked = false;
useState에서는 setClicked
라는 함수를 선언만 해주었으므로,
이 setClicked
를 호출해주거나 값을 넣어주는 행위는 아까 썼던 handleCategory
함수에서 진행됩니다.
- useState 선언
- 상태값을 관리할 변수
- 상태값을 갱신할 함수
- 상태값을 관리할 변수의 초기값
들이 선언된다.
- 동작시킬 요소에 이벤트 핸들러를 걸어준다.(onClick 같은것들)
- 이벤트 핸들러가 걸린 요소에 이벤트가 진행되면, 이벤트 핸들러 내부의 함수가 실행된다.(
setClicked(false) 가 되거나 setClicked(true)가 되거나
)4.
setClicked
의 결과에 따라clicked
의 값이 변경
대충 이런 흐름으로 이해중
끝일까요?아니요...
지금은 단지 .nav-category
클래스가 달린 요소를 클릭할때마다 clicked
의 값은 true거나 false가 될 뿐입니다.
NavDepth2
의 컴포넌트가 사라지거나 생겨야 하니까요.
<NavDepth2 show={clicked} />
NavDepth2
는 show
라는 임의의 프로퍼티,즉 props를 가지고 있습니다.
저 show
라는 프로퍼티의 값에 clicked
의 값을 넣어줄겁니다.
이제 NavDepth2
가 어떻게 생겨먹었는지 뜯어보러 갑니다.
import { useState, useEffect } from 'react';
import { MenuLink, ProductLink } from './Links.js';
const NavDepth2 = show => {
const [pageCategory, setPageCategory] = useState([]);
const [sub1Category, setSub1Category] = useState([]);
useEffect(() => {
fetch('./data/pageCategoryData.json', { method: 'GET' })
.then(res => res.json())
.then(data => {
setPageCategory(data);
});
fetch('./data/sub1CategoryData.json', { method: 'GET' })
.then(res => res.json())
.then(data => {
setSub1Category(data);
});
}, []);
return (
<div className={`nav-depth2 ${!show.show ? 'nav-disable' : ''}`}>
<div className="content-wrap">
<ul className="nav-depth3">
{pageCategory.map(list => (
<MenuLink key={list.id} list={list} />
))}
</ul>
{sub1Category.map(list => (
<ProductLink key={list.sub1_id} list={list} />
))}
</div>
</div>
);
};
export { NavDepth2 };
뭔가 또 많습니다. 지금은 이 부분만 봅니다.
<div className={`nav-depth2 ${!show.show ? 'nav-disable' : ''}`}>
.
.
</div>
NavDepth2
컴포넌트는 nav-depth2
의 클래스 네임을 가진 div 요소로 감싸져 있으며, 이 .nav-depth2
옆에 다중 클래스로 nav-disable
을 뗐다 붙였다 할 겁니다.
한마디로 클래스 명을 토글하는 것
저기서 console로 인자로 들어온 show
를 출력하면,간단히 이런 모습으로 출력됩니다.
Show:{show:false}
//들어온 인자 show: {Navdepth2의 props show : clicked의 값}
위쪽의 삼항 연산자에서 show.show
라고 작성한 것은 이러한 이유 때문입니다.
어쨌든 저 값은 true 아니면 false이기 때문에 조건을 !show.show
라고만 작성해줘도 저기서 true냐 false냐를 확인합니다.
만약
show.show
의 값이false
면
=><div className= "nav-depth2 nav-disable" >
만약
show.show
의 값이true
면
=><div className= "nav-depth2" >
이런 간단한 동작만으로 카테고리 메뉴의 토글을 구현했습니다.
애니메이션이 없다면 말이지....
여기서 드롭다운 애니메이션이 들어간다면 더 어려워질 것 같습니다.
비단 이럴 때뿐만 아니라, useState는 상태가 변경되는 컴포넌트를 관리할 때 용이하게 쓰입니다.
다음에는 NavDepth2에 적혀져 있던 것처럼,useState와 useEffect를 사용하여 데이터 관리를 하는 과정을 적겠습니다.