사용교재 : 벨로퍼트와 함께하는 모던 리액트
범위 : 5.1 ~ 5.5
노션 용량문제로 Migration
SPA라면 페이지가 하나란 뜻인데, 왜 라우터로 페이지를 나눌까?
단점
SPA) 앱의 규모가 커짐 → JS 파일 커짐 → 렌더링 속도 저하 → 코드 스플팅으로 해결가능
라우터) JS 실행 전에는 정보X , JS 캐싱 전에는 빈 화면 → SSR로 해결가능
BrowserRouter
컴포넌트로 감싸기
// ./src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom'; // * BrowserRouter 불러오기
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
// * App 을 BrowserRouter 로 감싸기
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
serviceWorker.unregister();
Route
: 특정 주소에 컴포넌트 연결<Route path="주소규칙" component={보여주고싶은 컴포넌트}>
// ./src/App.js
import React from 'react';
import { Route } from 'react-router-dom';
import About from './About';
import Home from './Home';
const App = () => {
return (
<div>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
</div>
);
};
export default App;
⇒ 이 경우 /
이 /home
에도 포함되기 때문에, 두 페이지가 동시에 나타난다!
→ 해결법) exact
!
exact
해당 속성이 true일 때, Route
컴포넌트는 경로가 정확히 일치할때만 연결시켜준다.
Link
: 누르면 다른 주소로 이동컴포넌트 클릭시 다른 주소로 이동시킨다.
(!) 리액트 라우터에서는 a
태그를 사용하면 안 된다
→ 정 쓰고 싶다면 onClick
이벤트로 관리한 뒤, 주소를 변경시켜주자
→ a
태그는 주소 이동 후, 페이지를 새로 불러오기 때문
→ 리액트 상태 초기화! ㄷㄷ
const App = () => {
return (
<div>
<ul>
<li>
<Link to="/">홈</Link>
</li>
<li>
<Link to="/about">소개</Link>
</li>
</ul>
<hr />
<Route path="/" exact={true} component={Home} />
<Route path="/about" component={About} />
</div>
);
};
/users/yrk
/about?details=true
// ./src/App.js
const App = () => {
return (
<div>
<Route path="/profiles/:username" component={Profile} />
</div>
);
};
// ./src/Profile.js
const profileData = {
gildong: {
name: '홍길동',
description: '전래동화의 주인공'
},
//...
};
const Profile = ({ match }) => {
// 파라미터를 받아올 땐 match 안에 들어있는 params 값을 참조.
const { username } = match.params;
const profile = profileData[username];
if (!profile) {
return <div>존재하지 않는 유저입니다.</div>;
}
return (
<div>
<h3>
{username}({profile.name})
</h3>
<p>{profile.description}</p>
</div>
);
};
export default Profile;
⇒ match
?
*{path: "/profiles/:username", url: "/profiles/velopert", isExact: true, params: Object}
1. path: "/profiles/:username"
2. url: "/profiles/velopert"
3. isExact: true
4. ▶params: Object
1. username: "velopert"*
⇒ 그 상위는?
*{match: Object, location: Object, history: Object, staticContext: undefined}
1. ▶match: Object
2. ▶location: Object
1. pathname: "/profiles/velopert"
2. search: ""
3. hash: ""
4. state: undefined
3. ▶history: Object
1. length: 1
2. action: "POP"
3. ▶location: Object
4. ▶createHref: *ƒ createHref() {}*
5. ▶push: *ƒ push() {}*
6. ▶replace: *ƒ replace() {}*
7. ▶go: *ƒ go() {}*
8. ▶goBack: *ƒ goBack() {}*
9. ▶goForward: *ƒ goForward() {}*
10. ▶block: *ƒ block() {}*
11. ▶listen: *ƒ listen() {}*
4. staticContext: undefined*
라우트 컴포넌트에 props
로 전달되는 location
객체의 search
값에서 조회가능
{
key: 'ac3df4', // not with HashHistory!
pathname: '/somewhere'
search: '?some=search-string',
hash: '#howdy',
state: {
[userDefined]: true
}
}
qs 라이브러리
Query를 문자열에서 객체로 변환
1) qs.parse(location.search.substr(1));
2) qs.parse(location.search, {ignoreQueryPrefix: true});
{pathname: "/about", search: "?detail=true", hash: "", state: undefined}
⬇️
{detail: "true"}
라우트 내부의 라우트
장점
setState
불필요// ./src/Profiles.js
const Profiles = () => {
return (
<div>
<Route
path="/profiles"
exact
render={() => <div>유저를 선택해주세요.</div>}
/>
<Route path="/profiles/:username" component={Profile} />
</div>
);
};
// App.js
const App = () => {
return (
<div>
<Route path="/" exact={true} component={Home} />
<Route path="/about" component={About} />
<Route path="/profiles" component={Profiles} />
</div>
);
};
라우트로 사용된 컴포넌트에게 전달되는 props
중 하나
이 객체를 통해 컴포넌트 내에 구현한 메소드 → 라우트 직접 접근 가능
(뒤로가기, 특정 경로로 이동, 이탈 방지 등)
function HistorySample({ history }) {
const goBack = () => {
history.goBack();
};
const goHome = () => {
history.push('/');
};
useEffect(() => {
console.log(history);
const unblock = history.block('정말 떠나실건가요?');
return () => {
unblock();
};
}, [history]);
//...
}
props
?{match: Object, location: Object, history: Object, staticContext: undefined}
match: Object
location: Object
history: Object
length: 6
action: "PUSH"
location: Object
createHref: ƒ createHref() {}
push: ƒ push() {}
replace: ƒ replace() {}
go: ƒ go() {}
goBack: ƒ goBack() {}
goForward: ƒ goForward() {}
block: ƒ block() {}
listen: ƒ listen() {}
라우트 컴포넌트가 아닌 곳에서 match
, location
, history
를 사용해야 할 때 사용
라우트 컴포넌트 아니지만, 자신 부모 기준의 라우트 props
가 전달됨
// ./src/WidthRouterSample.js
import { withRouter } from 'react-router-dom';
const WithRouterSample = ({ location, match, history }) => {
//...
};
export default withRouter(WithRouterSample);
여러 Route
들을 감싸서 그 중 규칙이 일치하는 단 하나의 라우트를 렌더링
→ 일치하는 것이 없을시 Not Found페이지 구현가능
const App = () => {
return (
<div>
<Switch>
<Route path="/" exact={true} component={Home} />
<Route path="/about" component={About} />
<Route path="/profiles" component={Profiles} />
<Route path="/history" component={HistorySample} />
<Route
// path 를 따로 정의하지 않으면 모든 상황에 렌더링됨
render={({ location }) => (
<div>
<h2>이 페이지는 존재하지 않습니다:</h2>
<p>{location.pathname}</p>
</div>
)}
/>
</Switch>
</div>
);
};
현재 경로와 Link
에서 사용하는 경로가 일치하는 경우, 특정 스타일 혹은 클래스를 적용할 수 있는 컴포넌트
사용법 : 컴포넌트에 activeStyle
속성을 통해 css
로 스타일링한다
<NavLink to="/profiles/gildong" activeStyle={{ color: 'red' }}>
→ activeClassName
을 통해 클래스명 부여도 가능
그 외
history.block
의 컴포넌트 버전useRoutes
v6라우트로 사용되지 않는 컴포넌트에서 라우트 관련 props
를 조회하는 훅(withRouter
)
사용 라이브러리 : use-react-router
import useReactRouter from 'use-react-router';
function RouterHookSample() {
const { history, location, match } = useReactRouter;
console.log({ history, location, match });
return null;
}
export default RouterHookSample;
→ 이후 릴리즈~
useHistory
, useLocation
, useParams
, useRouteMatch
등장 (from react-router-dom
)