React

자식 컴포넌트에서 부모 컴포넌트로 데이터 전달하기 / useState, useEffect 로 관리하기

아뵹젼 2022. 3. 31.

다음과 같이 헤더 컴포넌트 / 탭 컴포넌트 / 페이지 컴포넌트로 구성된 페이지를 구성 중이였다.

 

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, 컴포넌트별로 데이터 전달을 성공했다는데 의의를 둔다!

 

완성된 페이지는 다음과 같다.

탭의 변경 상태가 잘 전달되는 것을 볼 수 있다!!!!!

댓글