-
React Router위코드x원티드 2021. 9. 6. 13:57
- 라우팅: 다른 주소에 따라 다른 뷰를 보여주는 것
- SPA: Single Page Application, 페이지가 1개인 어플리케이션
React 빌드를 마친 후에는 JS 파일이 매 URL 마다 다른 컴포넌트를 그려준다. 그래서 번쩍임이 없는 URL 이동이 가능하다. 요런 기능 구현을 위해 React 에서는 주소마다 다른 뷰를 그려주는 React Router 가 필요하다
React Router introduces into your component the following objects: history, match, location
every view, component, or whatever that's wrapped by Router has these objects. <Router /> does its job as an Higher Order Component and wraps your components or views and injects these three objects as props inside them.
✔ Match
The match object contains information about how a <Route path> matched the URL
- params: (object) Key/value pairs parsed from the URL corresponding to the dynamic segments of the path
- isExact: (boolean) true if the entire URL was matched
- path: (string) The path pattern used to match. Useful for building nested <Route>s(?)
- url: (string) The matched portion of the URL. Useful for building nested <Link>s
localhost:3000/Home
match { path: "/Home", url: "/Home", isExact: true, params: {} }
isExact is true because the entire URL was matched, the params object is empty because we didn't pass anything into it, the path and the url key values are equal confirming that isExact is true
What if TopicsDetail?
localhost:3000/Topics/Topic1
match { path: "/Topics/:topicId", url: "/Topics/Topic1", isExact: true, params: { topicId: "Topic1" } }
isExact continues to be true because the entire URL was matched. params object brings the topicId info that was passed into the component
<Route path="/Topics/:topicId" component={TopicDetail} />
Pay attention to how the topicId is a variable. But where does it assume the Topic1 value? Simple, you're invoking it in an explicit way in TopicList Links
<Link to={`${match.url}/Topic1`}>Topic1</Link>
But, in which situation is the isExact false?
localhost:3000/Topics/Topic1/HelloWorldSection
match { path: "/Topics/:topicId", url: "/Topics/Topic1", isExact: false, params: { topicId: "Topic1" } }
In this situation we've introduced the /HelloWorldSection into the browser URL. What happens is that the Router doesn't know the full path to the HelloWorldSection so it routes you up until where it knows the way. isExact shows false telling you precisely that the "entire URL was not matched"
We've used the match.params.topicId to print in the screen our topic name. This is one of the most common usages for match. Of course it has a multitude of applications. Suppose we need to fetch an API with this topicId information. match.params 를 많이 이용한다
✔ Location
The location object represents where the app is now, where you want it to go, or even where it was
{ key: 'ac3df4', // not with HashHistory! pathname: '/somewhere', search: '?some=search-string', hash: '#howdy', state: { [userDefined]: true } }
It's also found on history.location but you shouldn't use that because it's mutable. A location object is never mutated so you can use it in the lifecycle hooks to determine when navigation happens. This is really useful for data fetching or DOM side effects
브라우저의 window.location 과 유사하다. 현재 페이지의 정보를 가지고 있다, location.search 로 현재 url 의 쿼리스트링을 가져올 수 있다
localhost:3000/mypage/ididid?name=kim#hashtag
location { pathname: "/mypage/ididid", search: "?name=kim", hash: "#hashtag", state: undefined }
You can provide locations instead of strings to the various places that navigate. Normally you just use a string, but if you need to add some "location state" that will be available whenever the app returns to that specific location, you can use a location object instead. This is useful if you want to branch UI based on navigation history instead of just paths (like modals)
<Link to="/somewhere" /> // usually all you need
const location = { pathname: '/somewhere', state: { fromDashboard: true } } <Link to={location} /> <Redirect to={location} /> history.push(location); history.replace(location);
✔ History
The history object allows you to manage and handle the browser history inside your views or components.
"history" which is one of only 2 major dependencies of React Router, and which provides several different implementations for managing session history in Javascript in various environments
The following terms are also used:
- browser history: A DOM-specific implementation, useful in web browsers that support the HTML5 history API
- hash history: A DOM-specific implementation for legacy web browsers
history objects typically have the following properties and methods:
- length: (number) The number of entries in the history stack
- action: (string) The current action (PUSH, REPLACE, or POP)
- push(path, [state]): (function) Pushes a new entry onto the history stack
- replace(path, [state]): (function) Replaces the current entry on the history stack
- goBack(): (function) Equivalent to go(-1)
- ...
브라우저의 window.history 와 유사하다. Stack 에 현재까지 이동한 url 경로들이 담긴 형태로 주소를 임의로 변경하거나 되돌아갈 수 있게 해준다
history { length: 6, action: "POP", location: {...}, push: f, ... }
It tells us that we've arrived here with a POP action, that the length of the object is 6 (as you navigate thru your app history grows to 50 and stops there, discarding the older entires and keeping its size each time the app pushes another history entry into the object)
React 에서 페이지를 이동시키는 경우의 수는 매우 다양하다
URL 에 직접 관련 변수를 넘겨줄 수도 있고, URL 에 표시하지 않은 채 state 로 관련 정보를 넘겨줄 수도 있다
나는 우선 url 에 id 를 넘겨주는 방식을 시도해보았다 (state 로 정보를 넘기는 방식은 추후)
// App.tsx function App() { return ( .... <BrowserRouter> <Nav /> <Route path='/' exact component={Home}></Route> <Route path='/movie/:id' component={MovieDetail}></Route> ); }
// MovieItem.tsx const moveToMovie = (id: number) => { history.push(`/movie/${id}`); };
클래스 컴포넌트에서는 주로 this.props.match 이런 식으로 접근하지만, 함수형 컴포넌트에서는 그렇지 않다
class Hello extends React.Component { render() { const { params } = this.props.match; return( <div> key1 : { params.key1 }, <br/> </div> .... export default Hello;
간단하게 url 파라미터(?) 로 넘어온 변수에 접근하는 방법은 useParams 나 useHistory Hook 을 사용하는 것이다
import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter as Router, Switch, Route, useParams } from "react-router-dom"; function BlogPost() { let { slug } = useParams(); return <div>Now showing post {slug}</div>; } ReactDOM.render( <Router> <Switch> <Route exact path="/"> <HomePage /> </Route> <Route path="/blog/:slug"> <BlogPost /> </Route> </Switch> </Router>, node );
하지만 위의 예제 코드를 보니, Route 컴포넌트 내부에서 간단히 useParams 를 사용해 변수를 받아온다. 나는 Route 와 컴포넌트를 나눠두었는데, 거기서 useParams() 를 사용하니 먹히지 않는다
If you wrap your component (functional or class) in withRouter, your props extend the RouteComponentProps from react-router,... To access the correct params, you have to extend the props like this:
RouteComponentProps<{ id?: string; }>
This will let typescript know, that you have a match props with an filed id, which is optional. You can now access them type safe with props.match.params.id. You can also extend that, to fit your other parameters.
위의 글을 그대로 참고한다. functional component 에서 route 관련 props 를 받아오기 위해서는 withRouter 로 감싸준다. typescript 를 사용했으므로, react-router 패키지에서 제공하는 타입인 RouteComponentProps 를 <>안에 넣어준다. props 를 받아와서 거기서 필요한 match 를 꺼내면 된다
const MovieDetail: React.FC<RouteComponentProps> = ({ ...props }): JSX.Element => { const { location, match } = props; }; export default withRouter(MovieDetail);
- type 선택이 안돼서 any 로 빼고 있다
참고
더보기https://gongbu-ing.tistory.com/44
https://reactrouter.com/web/api/Hooks/useparams
https://stackoverflow.com/questions/57312176/react-uri-params-in-functional-components
https://reactrouter.com/web/api/match
https://gongbu-ing.tistory.com/45
https://han7096.medium.com/react-router-v4-%EC%A0%95%EB%A6%AC-e9931b63dcae
https://www.freecodecamp.org/news/hitchhikers-guide-to-react-router-v4-4b12e369d10/
https://reactrouter.com/web/api/location
https://github.com/remix-run/history
.
'위코드x원티드' 카테고리의 다른 글
쉅정리 실행 컨텍스트, Closure, Hoisting, Scope, this (0) 2021.08.18 브라우저 동작 원리 (0) 2021.08.14 Event Loop (0) 2021.08.14 수업 정리 (0) 2021.08.08 Infinite Scroll 구현 (0) 2021.07.28