ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TS, React] styled-component에 ref 사용하기
    programing/Language 2020. 4. 17. 21:29

    안녕하세요, Einere입니다.

    (광고 차단 기능을 꺼주시면 감사하겠습니다.)


    포트폴리오용 웹페이지를 구현하다가 React의 ref를 사용하려고 삽질한 후기를 공유하도록 하겠습니다.

     

    원했던 그림

    제가 하고자 했던 것은, 메뉴 버튼을 클릭하면 메뉴 컨테이너가 나타나는 기능이었습니다.

    이를 위해 스타일을 클래스 네임으로 조작을 해야겠다고 생각했고, 클래스 네임을 조작하려면 컴포넌트에 대한 ref가 필요했습니다.

     

    Prop에 ref 추가하기

    위 요구사항을 위해 다음과 열심히 찾아서 다음과 같이 구현했습니다.

     

    // NavBar.tsx
    import * as React from "react";
    import {FunctionComponent, useCallback, useRef} from "react";
    import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
    import {faBars} from '@fortawesome/free-solid-svg-icons'
    import {Logo} from '../Logo/Logo';
    import {NavBarStyle} from "./NavBar.style";
    import {NavBarMenu} from "./NavBarMenu";
    
    export const NavBar: FunctionComponent = function () {
        const navBarMenuRef = useRef<HTMLDivElement>(null);
    
        const onClickHandle = useCallback(() => {
            console.log(navBarMenuRef.current);
        }, []);
    
        return (
            <NavBarStyle>
                <Logo width={150}/>
                <FontAwesomeIcon icon={faBars} className="nav-bar-menu-icon" onClick={onClickHandle}/>
                <NavBarMenu ref={navBarMenuRef}/>
            </NavBarStyle>
        );
    };
    
    

    네비게이션 바 컴포넌트인 NavBar에는 모바일 화면에서만 보이는 메뉴 아이콘(FontAwesomeIcon)이 있습니다.

    이 컴포넌트가 클릭되었을 때, 네비게이션 바의 여러 메뉴를 담고 있는 NavBarMenuclassList를 조작하려고 합니다.

     

    // NavBarMenu.tsx
    import * as React from "react";
    import {FunctionComponent, ForwardRefRenderFunction, Ref, ComponentPropsWithRef, useRef, forwardRef} from "react";
    import {NavBarMenuStyle} from "./NavBarMenu.style";
    import {NavBarMenuItem} from "./NavBarMenuItem";
    
    interface Prop {
        ref: Ref<HTMLDivElement>
    }
    
    export const NavBarMenu:FunctionComponent<Prop> = function ({ref}) {
        const menu = ["about", "skill", "project"];
        const MenuItems = menu.map((text, index) => <NavBarMenuItem text={text} key={index}/>);
    
        return (
            <NavBarMenuStyle className="nav-bar-menu" ref={ref}>
                {MenuItems}
            </NavBarMenuStyle>
        );
    };

    NavBarMenu컴포넌트는 NavBarMenuStyle이라는 div element를 reference합니다.

     

    // NavBarMenu.style.ts
    import styled from "styled-components";
    
    export const NavBarMenuStyle = styled.div`
      display: flex;
      height: 100%;
    
      @media(max-width: 992px) {
        display: none;
        
        &.show {
          position: absolute;
          background-color: rgba(0, 0, 0, 0.5);
          top: 70px;
          left: 0;
          width: 100%;
          height: 0;
          transition: 0.5s ease;
          overflow: hidden;
        }
      }
    `;
    

    NavBarStyle은 위와 같습니다.

     

    그런데 콘솔에 다음과 같은 오류가 떴습니다..

    음.. 함수형 컴포넌트는 ref를 받을 수 없다고 하네요.

    또 열심히 찾아봅니다..

     

    찾아본 결과, ref를 받기 위해서는 React.forwardRef함수를 사용해야 한다고 합니다.

     

    forwardRef 사용하기

    forwardRef를 사용한 코드는 다음과 같습니다.

     

    // NavBarMenu.tsx
    import * as React from "react";
    import {forwardRef} from "react";
    import {NavBarMenuStyle} from "./NavBarMenu.style";
    import {NavBarMenuItem} from "./NavBarMenuItem";
    
    export const NavBarMenu = forwardRef<HTMLDivElement>((props, ref)=> {
        const menu = ["about", "skill", "project"];
        const MenuItems = menu.map((text, index) => <NavBarMenuItem text={text} key={index}/>);
        return (
            <NavBarMenuStyle className="nav-bar-menu" ref={ref}>
                {MenuItems}
            </NavBarMenuStyle>
        );
    });
    

    forwardRef의 제네릭에 HTMLDivElement를 주고, 함수의 인자로서 propsref를 받으면 됩니다. 그리고 인자로 받은 refNavBarMenuStyleref속성으로 넘겨주면 됩니다.

    아, 참고로 forwardRef<HTMLDivElement>()의 반환값의 타입은 React.ForwardRefExoticComponent<React.RefAttributes<HTMLDivElement>>입니다. 참 기네요..

     

    테스트 하기

    메뉴 버튼을 누르니 정상적으로 출력됩니다.

     

    이거 해결할려고 몇시간동안 구글링했는지..ㅠㅠ

    댓글

Designed by black7375.