Route Matchers

WooBuntu·2021년 4월 6일
0

알고 쓰자 리액트

목록 보기
10/11

https://reactrouter.com/web/api/Route

Route

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";

ReactDOM.render(
  <Router>
    <div>
      <Route exact path="/">
        <Home />
      </Route>
      <Route path="/news">
        <NewsFeed />
      </Route>
    </div>
  </Router>,
  node
);

위와 같이 구현했다고 했을 때, app의 location이 /일 때는

<div>
  <Home />
  <!-- react-empty: 2 -->
</div>

app의 location이 /news일 때는

<div>
  <!-- react-empty: 1 -->
  <NewsFeed />
</div>

와 같은 UI계층 구조를 가진다.

react-empty 주석은 리액트가 null을 렌더링하는 것을 의미한다. 즉 Route는 path가 현재 URL과 일치하면 자신의 childern을, 일치하지 않으면 null을 렌더링하는 것으로 엄밀히는 어떠한 경우에도 반드시 렌더링된다.

이렇기 때문에 형제 관계에 있는 Route컴포넌트가 같은 컴포넌트를 자식으로 가지고 있을 때, 리액트는 해당 자식 컴포넌트들을 똑같은 인스턴스로 보기에 그 자식 컴포넌트들을 하나의 state를 공유하게 된다. 이를 의도한 것이 아니라면 각 Route컴포넌트에 key속성을 줘서 route가 변경될 때마다 새로운 컴포넌트 인스턴스를 생성하도록 하면 된다.

Route의 render methods

component

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";

// All route props (match, location and history) are available to User
function User(props) {
  return <h1>Hello {props.match.params.username}!</h1>;
}

ReactDOM.render(
  <Router>
    <Route path="/user/:username" component={User} />
  </Router>,
  node
);

아래의 render나 children과 다른 점은 router가 전달 받은 컴포넌트를 React.createElement로 넘겨 새로운 React element를 생성한다는 것이다. 즉, component prop안에 인라인으로 함수를 작성하면 렌더링을 할 때마다 컴포넌트의 이전 인스턴스를 언마운트 시키고 새로운 컴포넌트를 생성하여 마운트 시킨다는 뜻이다.(당연히 state도 초기화된다)

그러니 인라인 함수를 사용하는 것은 아래의 render나 children에서만 사용하자.

render

위에서 언급한 불필요한 재마운팅 과정 없이 인라인으로 렌더링하거나, 혹은 wrapping하기 위한 함수이다.

location이 match될 때 호출될 콜백을 전달하면 되고, 모든 route props에 접근 가능하다.

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";

// convenient inline rendering
ReactDOM.render(
  <Router>
    <Route path="/home" render={() => <div>Home</div>} />
  </Router>,
  node
);

// wrapping/composing
// You can spread routeProps to make them available to your rendered Component
function FadingRoute({ component: Component, ...rest }) {
  return (
    <Route
      {...rest}
      // 이렇게 전달한 routeProps가 아래의 routeProps가 되는 것
      render={routeProps => (
        <FadeIn>
          <Component {...routeProps} />
        </FadeIn>
      )}
    />
  );
}

ReactDOM.render(
  <Router>
    <FadingRoute path="/cool" component={Something} />
  </Router>,
  node
);

만약 component prop과 render prop을 동시에 사용하면 component가 우선하게 된다(동시에 안 쓰면 될 일;;)

children

path가 location과 일치하든 그렇지 않든 렌더링해야 할 때 사용한다. 그 외에는 render prop과 동일한 기능을 한다.

component든, render든, children이든 모두 똑같이 route props를 내려준다. 다만, 현재 URL과 route가 match되지 않는 경우 children에서는 match를 null값으로 내려 준다는 점에서 다르다.

이런 성질을 이용해서 아래와 같이 UI를 동적으로 변화시킬 수도 있다.

import React from "react";
import ReactDOM from "react-dom";
import {
  BrowserRouter as Router,
  Link,
  Route
} from "react-router-dom";

function ListItemLink({ to, ...rest }) {
  return (
    <Route
      path={to}
      // children으로 전달했으니 path가 일치하지 않더라도 렌더링된다.
      // 다만 path가 일치하지 않을 경우에는 match가 null로 전달된다.
      children={({ match }) => (
        <li className={match ? "active" : ""}>
          <Link to={to} {...rest} />
        </li>
      )}
    />
  );
}

ReactDOM.render(
  <Router>
    <ul>
      <ListItemLink to="/somewhere" />
      <ListItemLink to="/somewhere-else" />
    </ul>
  </Router>,
  node
);

children > component > render순의 우선순위를 가지고 적용된다.(당연히 하나만 골라 쓰자)

Route의 props들

  • path

    URL을 단일 string이나 string의 배열로 전달한다.
    path가 없는 Route는 항상 match된다.

  • exact

    현재 URL과 path가 '전부' 일치해야 하는지 여부

  • strict

<Route strict path="/one/">
  {/* /one과는 match되지 않는다 */}
  {/* /one/과는 match된다 */}
  {/* /one/two와도 match된다 */}
  <About />
</Route>
<Route exact strict path="/one">
  {/* /one과 match된다 */}
  {/* /one/과는 match되지 않는다 */}
  {/* /one/two와도 match되지 않는다 */}
  <About />
</Route>
  • location

Route컴포넌트는 현재의 history location(일반적으로 URL)과 path를 match시킨다. 하지만, 현재 URL과 다른 pathname을 가리키는 location 객체를 전달할 수도 있다.

animated transition을 구현한 것이 이런 성질을 이용한 사례 중 하나이다.

만약 Route컴포넌트가 Switch컴포넌트로 감싸져 있고 Switch로 전달된 location이 match되었다면, Route컴포넌트로 전달된 location prop은 Switch로 전달된 location이 덮어쓰게 된다.

  • sensitive

    path의 match여부를 판별할 때 대소문자를 구분한다.

Switch

https://reactrouter.com/web/api/Switch

현재 location과 일치하는 첫번째 Route나 Redirect를 렌더링한다.

import { Route } from "react-router";

let routes = (
  <div>
    <Route path="/about">
      <About />
    </Route>
    <Route path="/:user">
      <User />
    </Route>
    <Route>
      <NoMatch />
    </Route>
  </div>
);

위와 같이 구성하면 현재 경로가 /about이더라도 모두 match돼서 User와 NoMatch까지 전부 렌더링되지만,

import { Route, Switch } from "react-router";

let routes = (
  <Switch>
    <Route exact path="/">
      <Home />
    </Route>
    <Route path="/about">
      <About />
    </Route>
    <Route path="/:user">
      <User />
    </Route>
    <Route>
      <NoMatch />
    </Route>
  </Switch>
);

위와 같이 구성하면 About만 렌더링되는 것.

앞서 Route의 location props에서 본 것처럼, 이런 성질을 이용하여 animated tranistion을 구현하는 것도 가능하다.

location

Switch에 location 객체가 prop으로 전달되면 현재 location이 아닌 전달된 location을 기준으로 match여부를 판별한다.

children

Switch컴포넌트의 자식으로는 Route 혹은 Redirect만 올 수 있다.

Switch에서 Redirect를 자식으로 사용할 때 특이점은, Redirect가 path(Redirect에서는 from), exact, strict 등 Route가 location과 match할 때 사용하는 props들을 사용할 수 있다는 것이다.

0개의 댓글