리액트 라우터(React Router) - NavLink, Route, Hooks(useHistory, useLocation, useParams, useRouteMatch)

Noma·2021년 4월 21일
2
post-custom-banner

이전 포스팅과 이어집니다.

NavLink는 앞서 다룬 Link 컴포넌트와 비슷하지만 조금 기능이 더 부과된 것으로 보면 된다.

import React from 'react';
import { Link } from 'react-router-dom';

const Nav = (props) => (
    <ul>
        <li>
            <Link to="/">Home</Link>
        </li>
        <li>
            <Link to="/about">About</Link>
        </li>
    </ul>
);

export default Nav;

Link를 사용할 경우 개발자툴을 켜서 봤을 때 아래와 같다.

하지만 NavLink를 사용하면,

import React from 'react';
import { NavLink } from 'react-router-dom';

const Nav = (props) => (
    <ul>
        <li>
            <NavLink exact to="/">Home</NavLink>
        </li>
        <li>
            <NavLink to="/about">About</NavLink>
        </li>
    </ul>
);

export default Nav;

Home 클릭 시 해당하는 a 태그에 active라는 클래스가 부여되고,

About의 경우에도 동일하게 해당 태그에 클래스가 부여된다.

❗ 중요
<Route>에서 exact를 썼던 것과 같은 원리로, <NavLink>에서도 exact를 적절한 곳에 써줘야 한다. 그렇지 않으면, 위 예시의 경우 Home의 '/'는 About의 '/about'에도 걸리게 되므로 Home은 항상 active 클래스를 받게 된다.

다시 돌아와, 예시처럼 NavLink를 쓸 경우 얻을 수 있는 것이 무엇일까?

사용자가 현재 자신이 어떤 페이지에 있는지 직관적으로 이해할 수 있게, 네비게이션에 사용자가 위치하고 있는 곳을 표시해 줄 수 있다.

즉, 사용자가 위치하고 있는 곳이 active라는 이름의 class로 CSS 처리를 할 수 있게 된다.

//app.css
.active{
  background-color: rgb(255, 136, 136);
  text-decoration: none;
}

그러면 아래와 같은 효과를 구현할 수 있게 된다.

다음으로, 이전 포스팅에서 다루지 못한 일부 Route의 특성에 대해 정리하면서 Hook이 필요한 이유와 Hook의 종류에 대해 알아보겠습니다.

2. Route (+추가내용)

Route는 path에 따라 해당 UI를 보여주는 라우팅 기능을 가진 컴포넌트 이다.

2.1 Route render methods

props를 이용해 Route를 렌더링하는 방법엔 3가지가 있다. 이 중 한 가지만을 props으로 전달해줘야 한다. (여러 개 전달하면 안됨)

  1. component
<Route path="/" component={Home}/>

component prop을 이용해 렌더링할 컴포넌트를 전달하면, 라우터는 React.createElement를 사용해 새로운 React 컴포넌트를 생성하게 된다. 즉, 매번 렌더링될 때마다 새로운 컴포넌트를 만드는 것과 같다.

이는 기존의 컴포넌트를 업데이트 하는 것 대신에, 있던 컴포넌트를 언마운트 하고 새로운 컴포넌트를 마운트 한다는 뜻으로, 성능에 좋지 않을 수 있다. 따라서 인라인 렌더링을 위해 인라인 함수를 사용하는 경우에는, render 또는 children을 이용해라.

  1. render & children (Func)

render와 children을 사용하면 위에서 설명한 원치 않는 재마운팅을 하지 않고, 편리하게 인라인 렌더링 및 래핑이 가능하다. 하지만 이 방법들은 훅 도입 이전에 쓰던 방법으로 2.3 방법을 사용하도록 하자.

2.2 Route props

위 3가지 렌더링 메소드(2.3 제외)는 모두 다음과 같은 객체를 props으로 가진다. history, location, match, 총 3가지의 객체로 전달되어진다.

  • history: 해당 객체의 push, replace 함수를 통해 다른 경로로 이동하거나 앞 뒤 페이지로 전환할 수 있다.

  • location: 현재 경로에 대한 정보와 URL 쿼리(/about?foo=bar형식의)정보를 가지고 있는 객체이다.
    * URL Query : 어떤 키워드를 검색하거나, 페이지에 옵션을 전달할 때 사용된다.

  • match: 어떤 Route에 매칭이 되었는지에 대한 정보가 있고, params(about/:name형식의) 정보를 가지고 있는 객체이다.

2.3 가장 권장 되어지는 Route 사용법

이전 포스팅에서도 사용했던 방법으로, 컴포넌트를 Route의 자식 요소로 전달 하는 것이 가장 권장된다.

하지만 이 방법의 경우 위에서 다룬 3가지 방법(component, render, children)과는 달리 컴포넌트에 Route props(match, location, history) 객체가 전달되지 않는다.

이는 훅을 이용하면 간단히 사용할 수 있으므로, 걱정하지 말고 아래 방법으로 컴포넌트를 전달하도록 하자.

<Route path="/">
  <Home />
<Route/>

3. Hooks

Hook은 라우터의 상태에 접근하고 컴포넌트 내부에서 네비게이션을 수행할 수 있도록 하는 것으로, useParams, useLocation, useHistory, useRouteMatch가 있다.

3.1 useParams

기존에 Route로 사용되지 않은 컴포넌트에서 match, location, history 객체에 접근하려면 withRouter HoC로 컴포넌트를 감싸줘야 했다. 하지만 useParams가 추가되면서 손쉽게 match.params 객체에 접근할 수 있게 됬다.

📍__예시

import { useParams } from 'react-router';

const Topic = (props) => {
    const params = useParams();
      ...
    return (
      ...
    );
}

export default Topic;

이것을 이용한 URL Parameter 실습은 다음 포스팅에서 다룹니다.

3.2 useHistory

useHistory는 navigate 하는 데 사용할 수 있는 history 인스턴스에 대한 액세스를 제공한다.

📍__예시

import { useHistory } from "react-router-dom";

const Home=(props)=>{
  const history = useHistory();
  const handleClick=()=>{
    history.push("/topics");
  } //해당 페이지로 이동시킨다.

  return (
    <>
      <h1>Home</h1>
      <button onClick={handleClick}>
        Go topics
      </button>
    </>
  );
}
export default Home;

3.3 useLocation

useLocation은 현재 URL을 나타내는 location 객체를 반환한다.
(URL이 변경될 때마다 새 location를 반환)

📍__예시

import { useLocation } from "react-router";

const Topic=(props)=>{
  const location=useLocation();
  console.log(location.pathname); //출력 예: /topics/switch
  
  return (
    ...
  );
}
export default Home;

3.4 useRouteMatch

useRouteMatch는 match 객체의 값에 접근할 수 있게 해주는 hook이다. 사용 방법에는 두 가지가 있다.

하나는 Route 컴포넌트의 프로퍼티들(path, strict, sensitive, exact)을 가진 객체를 인자로 받는 방법으로, 만약 path 프로퍼티와 현재 페이지 URL이 일치할 경우 해당 path의 match객체를 반환하고 일치 하지 않으면 null을 반환해준다.

다른 하나는 아무 인자도 넘겨주지 않고 hook을 호출하는 방법이다. 이는 withRouter HoC로 match객체에 접근했을 때처럼 제일 가까운 부모 Route 컴포넌트의 match 값을 리턴해준다.

// 방법 1
// url이 "/topics/:topicID"와 일치할 때 Topic 컴포넌트를 렌더링하고 싶은 경우

import { useRouteMatch } from 'react-router';
const Topics=(props)=>{
  const match = useRouteMatch({
    path: '/topics/:topicId',
    strict: true,
  })
  return <div>{match ? <Topic match={match} /> : <NotFound />}</div>
}
export default Topics;
// 방법 2
// 제일 가까운 부모 <Route>컴포넌트의 match객체에 접근하고 싶은 경우
import { useRouteMatch } from 'react-router';
const Topic=(props)=>{
  const match = useRouteMatch();
  ...
}
export default Topic;

reference

profile
오히려 좋아
post-custom-banner

0개의 댓글