styled-components를 사용하여 NavLink의 active 스타일 넣기

Jaeminst·2022년 6월 21일
7
post-thumbnail
post-custom-banner

styled-components를 사용하기전 리액트와 css를 사용하여 NavLink를 구현하였다.

GitHub: https://github.com/Jaeminst/react-shoppingmall

참고 했던 블로그 링크

과정은 아래와 같다.

App.js파일에서 react-route-dom 라이브러리의 NavLink를 가져오고 <NavLink></NavLink>를 아래와 같이 작성.
react-router-dom 라이브러리의 NavLinkisActive라는 상태정보가 들어있어서 콜백으로 삼항연산을 통하여 문자열로 출력되게 만들었다.

/* App.js */
import './App.css';
import { BrowserRouter, NavLink } from 'react-router-dom';
import Home from './Pages/Home'

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <nav className='black-nav'>
          <NavLink className={({isActive}) => "nav-link" + (isActive? "a" : "")} to='/'>Home</NavLink>
        </nav>
      </BrowserRouter>
    </div>
  )
}

export default App;

다음은 App.css파일에서 className으로 스타일링을 해주었다.

/* App.css */
.black-nav {
  background: black;
  width: 100%;
  display: flex;
  color: white;
  padding: 20px;
  font-weight: 500;
  font-size: 20px;
  box-sizing: border-box;
}
.nav-link {
  color: white;
  padding: 20px;
  font-size: 20px;
  font-weight: 400;
  margin: 5px;
}
.nav-linka { 
  color: aqua;
  padding: 20px;
  font-size: 20px;
}

이제 라이브러리를 추가하고 CSS-IN-JS를 적용해보았다.

먼저 터미널에서 라이브러리를 추가하고

  • $ yarn add styled-components

그런다음 App.js에 먼저 import를 해준다.

import styled from "styled-components";

수정을 진행 했던 코드는 아래와 같다.
App.css파일과 import는 없애고 필요한 css 코드만 js파일에 넣어주었다.

/* App.js */
import styled from "styled-components";
/* import './App.css'; */
import { BrowserRouter, NavLink } from 'react-router-dom';
import Home from './Pages/Home'

const AppDiv = styled.div`
  text-align: center;
`
const BlackNav = styled.div`
  background: black;
  width: 100%;
  display: flex;
  color: white;
  padding: 20px;
  font-weight: 500;
  font-size: 20px;
  box-sizing: border-box;
`
const NavStyle = styled(NavLink)`
  color: white;
  padding: 20px;
  font-size: 20px;
  font-weight: 400;
  margin: 5px;
  outline: invert;
  &:link {
    transition : 0.5s;
    text-decoration: none;
  }
  &:hover {
    color: aquamarine;
  }
  &:active {
    color: aqua;
    position: relative;
    top: 2px;
  }
`

function App() {
  return (
    <AppDiv>
      <BrowserRouter>
        <BlackNav>
          <NavStyle className={({isActive}) => (isActive? "active" : "")} to='/'>Home</NavStyle>
        </BlackNav>
      </BrowserRouter>
    </AppDiv>
  )
}

export default App;

문제는 styled-components를 사용하여 css 코드를 js에 넣어 주었는데 active 스타일링이 적용되지 않는다.

원인을 파악해보니 NavLink를 스타일링 하여 사용했기 때문이다.

아래 코드는 react-route-dom 라이브러리에 NavLink의 리턴 코드이다.
createElement 함수 마지막 인자로 children을 전달하는데 원래라면 isActive 값을 넘겨주게 되어있다.

const NavLink = /*#__PURE__*/forwardRef(function NavLinkWithRef(_ref5, ref) {
  .
  .
  .
  
  return /*#__PURE__*/createElement(Link, _extends({}, rest, {
      "aria-current": ariaCurrent,
      className: className,
      ref: ref,
      style: style,
      to: to
    }), typeof children === "function" ? children({
      isActive
    }) : children);
}

하지만, styled-components로 만든 스타일 코드는 children 객체로 다른 값(title)을 넘겨주는것 같다.
styled-components 라이브러리의 코드는 보기 어렵게 난독화 되어있는지 원래 그런 코드인지 모르겠다..

그래서 여러가지 방법들을 써보았다.

혹시나 attrs()로 props를 넘겨받아 보아도 undefined

혹시나 className 콜백으로 만들어봐도 적용 X

<NavStyle className={({isActive}) => (isActive? "active" : "")} to='/'>Home</NavStyle>

혹시나 activeClassName으로 props 전달도 적용 X

const NavStyle = styled(NavLink).attrs(props => ({
  props: console.log(props),
}))`
.
.
.
function App() {
  return (
	<NavStyle activeClassName="active" to='/'>Home</NavStyle>
  )
}

콜백 없는 attrs 했더니 레퍼런스 에러

const NavStyle = styled(NavLink).attrs({
  activeClassName
})`

검색으로 같은 문제가 있었던 블로그를 찼았다.

https://yumyumlog.tistory.com/247

위 블로그를 보고 힌트를 받아서 문제를 해결할 수 있었다.

해결 방법은 간단했다.

먼저, 아래 코드는 styled-components 라이브러리 사용법을 익히고 처음 만들었던 버튼 스타일이다.

/* components/styled.js */
import styled from "styled-components";

const BlueButton = styled.button`
background-color: #eee;
border: none;
outline: none;
&:hover {
  color: blue;
  outline: auto;
  outline-color: blue;
}
&:active {
  color: blue;
  outline: auto;
  outline-color: blue;
  position: relative;
  top: 2px;
}
`;

export {
  BlueButton,
}

여기에 보면 버튼위 마우스를 올리거나 누를때의 스타일을 &:로 지정을 한다.
그래서 NavLink에서도 같은 방법을 썼는데 active만 동작을 하지 않아서 문제가 되었다.

:active 를 .active 로 바꾸었더니 동작을 한다....

&:active {                          &.active {
  color: aqua;						  color: aqua;
  position: relative;      =>         position: relative;
  top: 2px;                           top: 2px;
}                                   }

완성 Code

/* 변경 전 App.js */
import './App.css';
import { BrowserRouter, Routes, Route, NavLink } from 'react-router-dom';
import Home from './Pages/Home'
import About from './Pages/About'
import Contact from './Pages/Contact'
import Navigation from './Pages/Navigation'

function App() {
  
  return (
    <div className="App">
      <BrowserRouter>
        <nav className='black-nav'>
          <div className='logoName'>Jaeminst Shop</div>
          <NavLink className={({isActive}) => "nav-link" + (isActive? "a" : "")} to='/'>Home</NavLink>
          <NavLink className={({isActive}) => "nav-link" + (isActive? "a" : "")} to='/about'>About</NavLink>
          <NavLink className={({isActive}) => "nav-link" + (isActive? "a" : "")} to='/contact'>Contact</NavLink>
          <NavLink className={({isActive}) => "nav-link" + (isActive? "a" : "")} to='/Navigation'>Navigation</NavLink>
        </nav>
        <Routes>
          <Route path='/' element={<Home />} />
          <Route path='/about' element={<About />} />
          <Route path='/contact' element={<Contact text='props text'/>} />
          <Route path='/Navigation' element={<Navigation text='props text'/>} />
          <Route path='/Navigation/:id' element={<Navigation text='props text'/>} />
        </Routes>
      </BrowserRouter>
    </div>
  );
}

export default App;
/* 변경 후 App.js */
import styled from "styled-components";
import { BrowserRouter, Routes, Route, NavLink } from 'react-router-dom';
import Home from './Pages/Home'
import Detail from './Pages/Detail'
import About from './Pages/About'
import Contact from './Pages/Contact'
import Navigation from './Pages/Navigation'

const AppDiv = styled.div`
  text-align: center;
`
const BlackNav = styled.div`
  background: black;
  width: 100%;
  display: flex;
  color: white;
  padding: 20px;
  font-weight: 500;
  font-size: 20px;
  box-sizing: border-box;
`

const LogoName = styled.div`
  font-size: 30px;
  width: 300px;
  margin: 15px;
`

const NavStyle = styled(NavLink)`
  color: white;
  padding: 20px;
  font-size: 20px;
  font-weight: 400;
  margin: 5px;
  outline: invert;
  &:link {
    transition : 0.5s;
    text-decoration: none;
  }
  &:hover {
    color: aquamarine;
  }
  &.active {
    color: aqua;
    position: relative;
    top: 2px;
  }
`

function App() {
  
  return (
    <AppDiv>
      <BrowserRouter>
        <BlackNav>
          <LogoName>Jaeminst Shop</LogoName>
          <NavStyle to='/'>Home</NavStyle>
          <NavStyle to='/about'>About</NavStyle>
          <NavStyle to='/contact'>Contact</NavStyle>
          <NavStyle to='/navigation'>Navigation</NavStyle>
        </BlackNav>
        <Routes>
          <Route path='/' element={<Home />} />
          <Route path='/detail/:id' element={<Detail />} />
          <Route path='/about' element={<About />} />
          <Route path='/contact' element={<Contact text='props text'/>} />
          <Route path='/navigation' element={<Navigation text='props text'/>} />
          <Route path='/navigation/:id' element={<Navigation text='props text'/>} />
        </Routes>
      </BrowserRouter>
    </AppDiv>
  );
}

export default App;
profile
DevOps !
post-custom-banner

0개의 댓글