front/react

react-router-dom의 NavLink처럼 Link to와 현재 url이 일치할 때 styled-components 사용 하기 및 onClick 이벤트가 발생하면 css의 style이 변하게 설정

jeong_ga 2022. 7. 4. 17:55
<div className="nav">
  <span>대메뉴</span>
  <ul className="subNav>
    <li>소메뉴</li>
  </ul>           
</div>
  • 내가 구현하려고 했던 기능은 위와 같은 코드일 때에 ul-nav를 클릭하면 ul subNav가 보여지는 상황에서 현재 페이지의 url을 파악하여 suvNav>li의 폰트 색상이 인식
  • .subNav가 보일 때에는 아이콘이 ↓ 아래를 보게 css가 변화되는 기능을 넣고 싶었음!

참고 이미지

NavLink styled-components 사용

(참고 블로그)[https://yumyumlog.tistory.com/247]
Link는 const StyleLink = styled(Link) 로 작성하면 되는데! NavLink는 스타일드 컴포넌트를 적용할 수 없었다.
NavLink가 아닌 NavLink와 동일한 기능인 url을 인식하여 Link의 style이 변하게 만드는 코드를 따로 작성하여 사용하였다!

import { useState } from "react";
import { Link as RRDLink, useLocation } from "react-router-dom";
import styled from "styled-components";


const Link = ({ children, isActive, ...props }) => {
  return <RRDLink {...props}>{children}</RRDLink>;
};

const StyledSubLink = styled(Link)`
  text-decoration: none;
  font-family: ${({ theme }) => theme.font.point};
  font-size: 14px;
  color: ${(props) => (props.isActive ? "#64C5B1" : "#101038")};
`;

function NavInnerSub({ item }) {

  const { pathname } = useLocation();

  return (
    <NavInnerButton>
      <div className="nav">
        <span>대메뉴</span>
          <ul className="subNav>
                <li to={item.url} isActive={pathname === item.url}>
                  {item.subName}
                </li>
          </ul>
      </div>
    </NavInnerButton>
  );
}
export default NavInnerSub;

onClick 이벤트가 발생하면 style이 변하는 기능

onClick 이벤트가 발생하면 아이콘이 ˃ -> ˅ 로 변하며 숨겨졌던 navSub가 보이도록 하는 기능 + transition

  • ul의 높이를 0에서 auto로 설정할 때에는 transition이 적용되지 않아 라이브러리를 통해 해결하였음. (이전 게시글 참고)
import { useState } from "react";
import styled from "styled-components";
import AnimateHeight from "react-animate-height";

import NavPartList from "./NavPartList";

const NavInnerButton = styled.button`
  width: 100%;
  height: auto;
  padding: 0;
  padding-right: 25px;
  margin-top: 25px;
  margin-left: 30px;
  text-decoration: none;
  border: none;
  background-color: transparent;
  text-align: left;
  cursor: pointer;

  .sub_list {
    ul {
      width: 180px;
      margin-left: 30px;
      padding-bottom: 5px;
    }
    li {
      width: 100%;
      height: auto;
      line-height: 41px;
    }
    // NavInner Name
    & > div {
      position: relative;
    }
    // arrow
    & > div:first-child:after,
    & > div:first-child::after {
      display: block;
      content: "";
      position: absolute;
      right: 0;
      top: 10px;
      width: 7px;
      height: 7px;
      border-top: 1px solid ${({ theme }) => theme.colors.pointGray};
      border-left: 1px solid ${({ theme }) => theme.colors.pointGray};
      transform: rotate(135deg);
      transition: all 0.3s ease-in;
    }
  }

// true가 되어 className에 .sub_list_show가 설정되면 보이는 css
  .sub_list_show {
    width: 100%;
    // arrow
    & > div:first-child:after,
    & > div:first-child::after {
      transform: rotate(225deg);
    }
  }
`;


function NavInnerSub({ item }) {
// react-animation-height 라이브러리용 state
  const [height, setHeight] = useState(0);

// button클릭 시 이벤트 적용 여부를 확인하기 위한 state
  const [more, setMore] = useState(false);

  const onClickMore = () => {
    setMore(!more);
    setHeight(height === 0 ? "auto" : 0);
  };

  return (
    <NavInnerButton onClick={onClickMore}>
    // more로 현재를 확인한 후, sub를 봐야할 부분은 sub_list_show이라는 className 설정
      <div className={more ? "sub_list sub_list_show" : "sub_list"}>
          // nav name 컴포넌트
        <NavPartList item={item} />

        // ul>li 부분
          <ul>
            {item.subNav.map((item, key) => (
              <li key={key}>
                <StyledSubLink to={item.url} isActive={pathname === item.url}>
                  {item.subName}
                </StyledSubLink>
              </li>
            ))}
          </ul>
      </div>
    </NavInnerButton>
  );
}
export default NavInnerSub;

작성한 코드 전문

import { useState } from "react";  
import { Link as RRDLink, useLocation } from "react-router-dom";  
import styled from "styled-components";  
import AnimateHeight from "react-animate-height";

import NavPartList from "./NavPartList";

const Link = ({ children, isActive, ...props }) => {  
return <RRDLink {...props}>{children};  
};  
const NavInnerButton = styled.button\`  
width: 100%;  
height: auto;  
padding: 0;  
padding-right: 25px;  
margin-top: 25px;  
margin-left: 30px;  
text-decoration: none;  
border: none;  
background-color: transparent;  
text-align: left;  
cursor: pointer;

.sub\_list {  
ul {  
width: 180px;  
margin-left: 30px;  
padding-bottom: 5px;  
}  
li {  
width: 100%;  
height: auto;  
line-height: 41px;  
}  
// NavInner Name  
& > div {  
position: relative;  
}  
// arrow  
& > div:first-child:after,  
& > div:first-child::after {  
display: block;  
content: "";  
position: absolute;  
right: 0;  
top: 10px;  
width: 7px;  
height: 7px;  
border-top: 1px solid ${({ theme }) => theme.colors.pointGray};  
border-left: 1px solid ${({ theme }) => theme.colors.pointGray};  
transform: rotate(135deg);  
transition: all 0.3s ease-in;  
}  
}

.sub\_list\_show {  
width: 100%;  
// arrow  
& > div:first-child:after,  
& > div:first-child::after {  
transform: rotate(225deg);  
}  
}  
\`;

const StyledSubLink = styled(Link)`text-decoration: none; font-family: ${({ theme }) => theme.font.point}; font-size: 14px; color: ${(props) => (props.isActive ? "#64C5B1" : "#101038")};`;

function NavInnerSub({ item }) {  
const { pathname } = useLocation();  
const \[height, setHeight\] = useState(0);  
const \[more, setMore\] = useState(false);  
const onClickMore = () => {  
setMore(!more);  
setHeight(height === 0 ? "auto" : 0);  
};

return (  
<NavInnerButton  
aria-expanded={height !== 0}  
aria-controls="example-panel"  
onClick={onClickMore}  
\>  
<div className={more ? "sub\_list sub\_list\_show" : "sub\_list"}>  



{item.subNav.map((item, key) => (

-     
    <StyledSubLink to={item.url} isActive={pathname === item.url}>  
    {item.subName}  



))}





);  
}  
export default NavInnerSub;