다음과 같이 헤더 컴포넌트 / 탭 컴포넌트 / 페이지 컴포넌트로 구성된 페이지를 구성 중이였다.
App.js 에서 작성한 컴포넌트의 구조는 다음과 같다.
<div className="App">
<BrowserRouter>
<Header />
<Tab />
<Routes>
// 라우터 생략
</Routes>
</BrowserRouter>
</div>
4개의 탭을 클릭했을 때 각 탭이 active 되는 것은 탭 컴포넌트에서 잘 구현하였는데,
헤더에서 타이틀과 로그인을 클릭했을 때, 타이틀의 경우 첫 번째 탭이 활성화되도록,
로그인의 경우 모든 탭이 비활성화되도록 구현하고 싶었다.
그러기 위해서 App.js 에서 탭을 관리할 수 있는 변수를 따로 만들고
<헤더 컴포넌트에서 클릭 발생 -> App.js 로 전달 -> 탭 컴포넌트로 전달 >
의 로직을 짜야할 것 같았다.
아직 리액트 hook 에 대한 개념이 부족한지라, 우여곡절이 정말 많았다.😂🤣
먼저, 부모 컴포넌트인 App.js 에서 tab 메뉴가 변경되었을 때 리렌더링을 진행하여
Tab.js 에 바뀐 탭의 값을 props 로 넘겨줄 수 있도록 Tab 변수를 useState 로 구현하였다.
const [tabMenu, setTabMenu] = useState(0);
자식 컴포넌트에서 부모 컴포넌트에 데이터를 넘겨주는 방법은, 부모 컴포넌트의 함수를 props 로 넘겨주는 것이었다.
따라서 내부에서 탭 메뉴를 변경해주는 setTabMenu() 를 실행하는 함수를 App.js 에서 하나 만들었다.
const tabMenuChange = (tabMenu) => {
setTabMenu(tabMenu);
};
그런 다음, Header.js 와 Tab.js 의 props 로 tabMenu 와 부모의 함수를 넘겨주었다.
<Header tabMenuChange={tabMenuChange} />
<Tab tabMenuChange={tabMenuChange} />
이제 Header.js 에서 타이틀과 로그인 텍스트에 onClick 함수를 구현해주었다.
function Header(props) {
function setTabMenu(menu) {
props.tabMenuChange(menu);
}
return (
<div className="Header">
<div className="Container">
<Link to="/login">
<div
className="Container-Text"
onClick={() => {
setTabMenu(0);
}}>로그인
</div>
</Link>
</div>
<Link to="/">
<div
className="Header-Text"
onClick={() => {
setTabMenu(1);
}}> Findog
</div>
</Link>
</div>
);
}
export default Header;
onClick 이벤트가 발생하면 props의 tabMenuChange 함수를 실행한다.
이때 타이틀의 경우 첫 번째 탭을 활성화하고 싶어 인자로 1을,
로그인의 경우 아무 탭도 활성화 하고 싶지 않아 0을 주었다.
이제, 헤더에서 클릭이 일어나면 App.js 부모 컴포넌트에서 tabMenu 가 변경되게 된다.
const tabMenuChange = (tabMenu) => {
setTabMenu(tabMenu);
};
useEffect(() => {
console.log("app rerendering", tabMenu);
}, [tabMenu]);
그리고, tabMenu 가 변경될 때 재렌더링이 제대로 일어나는 지 확인하기 위해 useEffect 를 사용하여 log 를 출력해보았다. 잘 된다😁
부모 컴포넌트가 재렌더링이 되면, 자식 컴포넌트들도 모두 재렌더링이 일어나게 된다.
따라서 Tab.js 가 재렌더링이 된다.
Tab.js 전체코드는 다음과 같다.
function Tab(props) {
const [menu, setMenu] = useState(0);
useEffect(() => {
console.log("useeffect", props.tabMenu);
setMenu(props.tabMenu);
}, [props.tabMenu]);
const setTabMenu = (menu) => {
props.tabMenuChange(menu);
};
return (
<div className="Tab-Wrap">
<div className="Tab-Bar">
<ul className="Tab">
<Link to="/">
<li
className={classNames(Tab, `${menu === 1 ? "active" : ""}`)}
onClick={() => {
setMenu(() => 1);
setTabMenu(1);
}}
>
유기동물
</li>
</Link>
<Link to="/board">
<li
className={classNames(Tab, `${menu === 2 ? "active" : ""}`)}
onClick={() => {
setMenu(() => 2);
setTabMenu(2);
}}
>
게시판
</li>
</Link>
<Link to="*">
<li
className={classNames(Tab, `${menu === 3 ? "active" : ""}`)}
onClick={() => {
setMenu(() => 3);
setTabMenu(3);
}}
>
유기센터
</li>
</Link>
<Link to="/mypage">
<li
className={classNames(Tab, `${menu === 4 ? "active" : ""}`)}
onClick={() => {
setMenu(() => 4);
setTabMenu(4);
}}
>
마이페이지
</li>
</Link>
</ul>
</div>
</div>
);
}
export default Tab;
Tab.js 내부에서 tabMenu 를 관리하는 변수를 만들어주었다. 이 변수를 통해 각 탭이 활성화될 수 있다.
const [menu, setMenu] = useState(0);
그리고, useEffect 를 통해, props.tabMenu 가 바뀐 경우에만 내부의 menu 변수를 변경하도록 하였다.
useEffect(() => {
setMenu(props.tabMenu);
}, [props.tabMenu]);
이제, menu 변수의 값에 따라 Tab 이 활성화 된다.
const setTabMenu = (menu) => {
props.tabMenuChange(menu);
};
<Link to="/">
<li
className={classNames(Tab, `${menu === 1 ? "active" : ""}`)}
onClick={() => {
setMenu(() => 1);
setTabMenu(1);
}}
>
유기동물
</li>
</Link>
menu 의 값에 따라 각 탭의 className 에 active 값을 붙혀주어,
css 에서 active 값을 관리할 수 있도록 하였다.
또한, router-link 를 통해 쉽게 이동할 수 있도록 하였다.
그리고, 탭이 변경되면 App.js 부모 컴포넌트의 함수를 실행하여 부모 컴포넌트의 tab 변수를 업데이트하게 하였다.
아직 hook 에 익숙하지 않아, 최선의 코드가 아닐 수도 있다.
그치만 리액트 입문자로서 useEffect, useState, 컴포넌트별로 데이터 전달을 성공했다는데 의의를 둔다!
완성된 페이지는 다음과 같다.
탭의 변경 상태가 잘 전달되는 것을 볼 수 있다!!!!!
'React' 카테고리의 다른 글
헷갈리는 styled component 기능 (0) | 2022.05.14 |
---|---|
Axios Delete 에서 Body 에 data 넣는법 (0) | 2022.05.12 |
React 에서 새로고침 시 탭이 첫 번째 항목으로 이동하는 문제 해결 (0) | 2022.04.10 |
React 에서 중첩 라우터 사용하기 (0) | 2022.04.08 |
useEffect, useMemo, useState, useCallback, React.Memo() 개념 및 차이점 (0) | 2022.03.18 |
댓글