# react-router
> [reactrouter官方](https://reactrouter.com/web/api/Route/component)
> 主要功能為:**path與當前網址相同時呈現UI**
## Switch
> 用於包裝Route。switch會依照Route順序進行匹配,當第一個符合則不繼續匹配且顯示該頁component;反之若無使用switch包裝,則匹配符合者皆render於頁面
```
<Switch> ... </Switch>
```
### Why do we need to use Switch ?
1. 在不使用switch時,若path符合者皆會render顯示

```
< >
< Route path={} component={} />
< Route path={} component={} />
...
< />
//可利用containers/routes/index.js將<switch>改成<>
```
2. 使用switch時當第一個path是"/"因此網頁一直無法正常顯示,就算更改網址處仍會匹配到"/"畫面(無使用exact)
```
< Switch>
<Route path="/" component={Home} />
...
< /Switch>
//可利用containers/routes/index.js將Route login,logout移除使home於第一個匹配位置,並移除exact參數
```
- 以containers/routes/index.js為例,會造成網址處無論輸入/library、/capture 等等,皆會傳回home再重導向至live
3. 使用switch時若將live移動至"/"上,第一個path則為"/live"因此網頁首先匹配到"/live"頁面成功進入但切換頁面仍為live頁面
```
< Switch>
<Route path="/live" component={Live} />
<Route path="/" component={Home} />
...
< /Switch>
//可利用containers/routes/index.js將AuthenticatedRoute live移動至Route home上方
```
- 以containers/routes/index.js為例,第一個匹配path為live後為home,且由於home重導向至live,因此無論網址處輸入/library、/capture等等,仍會傳回至live頁面
4. 依照第三點,將alert頁面移動至第一個,則輸入匹配順序依照alert -> live -> home
```
< Switch>
<Route path="/alert" component={Alert} />
<Route path="/live" component={Live} />
<Route path="/" component={Home} />
...
< /Switch>
//可利用containers/routes/index.js將AuthenticatedRoute alert移動至Route live及home上方
```
- 以containers/routes/index.js為例,第一個匹配path為alert後為live再至home,且由於home重導向至live,因此無論網址處輸入/library、/capture等等,仍會傳回至live頁面,除了alert頁面會成功引導。
5. 依照第四點,將home加上exact參數,使網址處完全符合home path時才會render
```
< Switch>
<Route path="/alert" component={Alert} />
<Route path="/live" component={Live} />
<Route exact path="/" component={Home} />
...
< /Switch>
//可利用containers/routes/index.js將AuthenticatedRoute 將Route home加上exact參數
```
- 以containers/routes/index.js為例,原本網址處輸入/library、/capture等等會回傳至live頁面,加入exact後頁面皆能成功引導。
## Route
> 當Route判斷到網址的路徑(path與網址)相符,要渲染(route render methods)時都會將物件(route.props)給傳進該組件中。
> **Route props:**
> - match :url、path、isExact
> -- url 匹配url,用於Link
> -- path 匹配path,用於Route
> -- isExact 是否精確,若Route時有加入exact,則返回true;反之則false
>
> - location (object) : { pathname, search, hash, key }
> - history
- exact 精確
- path 路徑
> 設定與網址匹配的路徑,相同則render
> **Route render methods:**
> - component
> -- Route會利用React.createElement創建一個新的React元素。每個渲染中創建一個新組件。因此在搭配inline fuction時會不斷unmount,mount,使效率變低。
> ```
> FaceAI: // path匹配成功時,render component
> < Route path="/login" component={SignIn} />
> ```
> - render
> -- 使用inline function時使用,避免組件重複創建,有效達到更新。
> ```
> FaceAI: // render={()=>(< >< />)}
> < Route render={props => (
> < BasicLayout>
> < NoMatch {...props} />
> < /BasicLayout>
> )}/>
> ```
> - v5後新增:children
> -- 無論path是否match,皆會渲染。使你可以根據是否match來動態調整界面。
> ```
> 官網EX: // match匹配時active則新增,反之為null
> < Route
> path={to}
> children={({ match }) => (
> < li className={match ? "active" : ""}>
> < Link to={to} {...rest} />
> < /li>
> )}
> />
> ```
### FaceAI應用
- 利用<switch包裝,使第一個path符合時則不繼續匹配
- 使用<Route設定path,component
```
## containers/routes/index.js
const Routes = () => (
<Switch>
<Route path="/login" component={SignIn} />
<Route path="/logout" component={Logout} />
<AuthenticatedRoute exact path="/" component={Home} layout={BasicLayout} />
{/* Live */}
<AuthenticatedRoute path={ROUTES.live} component={Live} layout={BasicLayout} />
{/* Alert */}
<AuthenticatedRoute path={ROUTES.alert} component={Alert} layout={BasicLayout} />
{/* Comparison */}
<AuthenticatedRoute path={ROUTES.comparison} component={Comparison} layout={BasicLayout} />
{/* Library */}
<AuthenticatedRoute path={ROUTES.library} component={Library} layout={BasicLayout} />
{/* Face */}
<AuthenticatedRoute path={ROUTES.face} component={Face} layout={BasicLayout} />
{/* Capture */}
<AuthenticatedRoute path={ROUTES.capture} component={Capture} layout={BasicLayout} />
{/* Settings */}
<AuthenticatedRoute path={ROUTES.settings} component={Settings} layout={BasicLayout} />
{/* Doc */}
<AuthenticatedRoute path="/doc" component={Doc} layout={BasicLayout} />
{/* NoMatch */}
<Route // Route render
render={props => (
<BasicLayout>
<NoMatch {...props} />
</BasicLayout>
)}
/>
</Switch>
)
```
- Routes中用到< AuthenticatedRoute />
- 判斷routeAuthenticated回傳權限為true/false
- true: < Component {...props} />
- Component為LoadableComponent(props) 將{...props}皆render出來
- false: < NoAuth>
- containers/routes/NoAuth -> 傳送至403頁面
```
## components/AuthenticatedRoute/index.js
render() {
const { layout: Layout, component: Component, routeAuthenticated, ...rest } = this.props
return (
<Route
{...rest}
render={props => (
<Layout>{routeAuthenticated ? <Component {...props} /> : <NoAuth />}</Layout>
)}
/>
)
}
```
- AuthenticatedRoute使用routeAuthenticated
- 利用checkAuth回傳值判斷有無權限true/false
```
## containers/routes/AuthenticatedRoute
const routeAuthenticated = checkAuth(role.code, `route.${path}`)
```
- routeAuthenticated使用checkAuth(role.code, 'route.${path}')
- checkAuth(role(str),name(str))
- role為登入者角色ex:root / admin / staff / faceadmin
- name為路徑ex:route./live / route./library / route./capture
- name拆分為key=>[route /capture]
- ROLES_AUTHORITY是每個path對應到的使用者登入權限ex: <path>:[root,admin,staff,faceadmin] // json{route: {…}, action: {…}}
- 判斷ROLES_AUTHORITY內key(path)與傳送來的name(path)
- auth && auth.indexOf(role) > -1
- 判斷auth(權限角色)內是否存在role(role.code~=登入者角色)
```
## utils/index.js
export const checkAuth = (role, name = '') => {
const keys = name.split('.')
let auth = ROLES_AUTHORITY
keys.forEach(key => {
if (key && auth[key]) {
auth = auth[key]
} else {
auth = []
}
})
return auth && auth.indexOf(role) > -1
}
```
## WithRouter
> Higher Order Component
> 能將Route內location、 history等資訊 作為props傳入component,一般只有包在Route裡面的component才能拿到router的資訊,如果是沒有被包在Route裡面的component需要取得router的資訊就可以透過withRouter
### FaceAI應用
```
## containers/layout/ScrollToTop.js
class ScrollToTop extends PureComponent {
static propTypes = {
location: object.isRequired,
children: node.isRequired
}
componentDidUpdate(prevProps) {
const {
location,
location: { pathname }
} = this.props // pathname 新path
const {
location: { pathname: prevPathname }
} = prevProps // prevPathname 原path
if (pathname !== prevPathname) {
this.scrollTo(location) // location: state.router.location
}
console.log('withRouter', withRouter)
}
scrollTo = ({ hash } = {}) => {
// hash: state.router.location.hash
// 網址處若有#字符號為hash
console.log('type', hash)
if (hash) {
const hashDom = window.document.querySelector(hash)
hashDom && hashDom.scrollIntoView()
} else {
window.document.body.scrollTop = 0 // 獲取當前頁面滾動條縱坐標的位置 chrome只認得: document.body.scrollTop;標準瀏覽器認得:document.documentElement.scrollTop
// var scrollTop=document.body.scrollTop+document.documentElement.scrollTop -> 當一個值取得時令一個一定為0,因此可使用此公式判斷
window.scrollTo(0, 0) // 滾動至某座標 window.scrollTo(x-coord橫座標, y-coord縱座標 )
}
}
render() {
const { location, children } = this.props // location: router.location.key
return <div key={location.key}>{children}</div>
// location.key代表每一次render的頁面key children為每頁component內容
}
}
export default withRouter(ScrollToTop)
```
```
## component/BasicLayout
<ScrollToTop>{children}</ScrollToTop>
// -> <div key={location.key}>{children}</div>
```
# react-router-dom
> 依賴react-router套件且核心亦為react-router,
### Link
> 像html中<a> 透過點擊來跳轉頁面
> - to 移動至路徑
```
## component/Header/index.js
<Link to="/logout">
<Icon type="logout" />
<FormattedMessage id="App.header.logout" />
</Link>
## component/NoAuth/index.js
<Link to="/">
<Button type="danger" size="large" ghost>
{intl.formatMessage({ id: 'App.403.backBtn' })}
</Button>
</Link>
```
### Redirect
> 用於重導向至某路徑
> - from 若沒設定則為當前路徑
> - to 移動至路徑
```
## component/Home/index.js
const Home = () => <Redirect to="/live" />
```