老牌的应用获取新页面需要发送HTTP请求,由服务器返回HTML
在单页面应用(single page apps)中,我们实际总是在同一页面, 如果发送HTTP GET请求,也只是为了获取JSON数据
每个视图(view)最好都有自己的地址,这样方便用户收藏书签,浏览器返回到上一页面也得以实现
安装React-router-dom
npm install react-router-dom
一个使用React-router的示例
import React from 'react'
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'
const Home = () => (
<div>
{' '}
<h2>TKTL notes app</h2>{' '}
</div>
)
const Notes = () => (
<div>
{' '}
<h2>Notes</h2>{' '}
</div>
)
const Users = () => (
<div>
{' '}
<h2>Users</h2>{' '}
</div>
)
const App = () => {
const padding = {
padding: 5,
}
return (
<Router>
<div>
<Link style={padding} to="/">
home
</Link>
<Link style={padding} to="/notes">
notes
</Link>
<Link style={padding} to="/users">
users
</Link>
</div>
<Switch>
<Route path="/notes">
<Notes />
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
<div>
<i>Note app, Department of Computer Science 2021</i>
</div>
</Router>
)
}
export default App
路由,或者基于url的条件渲染,通过将组件放在Router组件中来实现
Router实际上是导入BrowserRouter后的别称
import { BrowserRouter as Router,
Switch, Route, Link } from 'react-router-dom'
BrowserRouter is a Router that uses the HTML5 history API (pushState, replaceState and the popState event) to keep your UI in sync with the URL.
在Router内部,我们通过Link组件,修改地址栏的url
<Link to="/notes">notes</Link>
根据地址栏URL来渲染对应的组件,是在Route组件中实现的
<Route path="/notes">
<Notes />
</Route>
将所有基于url渲染的组件都放在Switch组件里, 即这些组件的视图区域都在Switch里
<Switch>
<Route path="/notes">
<Notes />
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
注意,在Switch内部的顺序很重要,如果将path=”/“放在第一个,所有以’/‘开头的url都能与之匹配,导致其他所有组件都得不到渲染。
同理,如果url A包含了B, 且A比B长,那么A要放前面,否则永远匹配不到A
<Switch>
<Route path={'/anecdotes/:id'}>
<Anecdote anecdote={anecdote} />
</Route>
<Route path={'/anecdotes'}>
<AnecdoteList anecdotes={anecdotes} />
</Route>
</Switch>
Parameterized route
每条li都是可以点击的Link, 当点击时,修改地址栏的url
const Notes = ({notes}) => (
<div>
<h2>Notes</h2>
<ul>
{notes.map(note =>
<li key={note.id}>
<Link to={`/notes/${note.id}`}>{note.content}</Link>
</li>
)}
</ul>
</div>
)
Route通过**:id**
传递参数(express style)
<Switch>
<Route path="/notes/:id">
<Note notes={notes} />
</Route>
<Route path="/notes">
<Notes notes={notes} />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
通过**useParams()**
获取url的参数
import {
// ...
useParams
} from "react-router-dom"
const Note = ({ notes }) => {
const id = useParams().id
const note = notes.find(n => n.id === Number(id))
return (
<div>
<h2>{note.content}</h2>
<div>{note.user}</div>
<div><strong>{note.important ? 'important' : ''}</strong></div>
</div>
)
}
useHistory
通过useHistory, 组件可以访问history对象,通过向history对象push一个地址,将url修改为对应的地址并渲染对应的组件。
import {
// ...
useHistory
} from 'react-router-dom'
const Login = (props) => {
const history = useHistory()
const onSubmit = (event) => {
event.preventDefault()
props.onLogin('mluukkai')
history.push('/')
}
return (
<div>
<h2>login</h2>
<form onSubmit={onSubmit}>
<div>
username: <input />
</div>
<div>
password: <input type='password' />
</div>
<button type="submit">login</button>
</form>
</div>
)
}
Redirect
如果user不存在,则不渲染Users组件,而是重定向到/login, 即修改地址为/login并渲染
<Route path="/users">
{user ? <Users /> : <Redirect to="/login" />}
</Route>
Parameterized route revisited
使用useRouteMatch获取匹配的url信息
将Router移到App外面
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
)
在App组件内部使用useRouteMatch
import {
// ...
useRouteMatch
} from "react-router-dom"
const App = () => {
// ...
const match = useRouteMatch('/notes/:id')
const note = match
? notes.find(note => note.id === Number(match.params.id))
: null
return (
<div>
<div>
<Link style={padding} to="/">home</Link>
// ...
</div>
<Switch>
<Route path="/notes/:id">
<Note note={note} />
</Route>
// ...
</Switch>
</div>
)
}
每次浏览器url更改时,都会执行如下代码
const match = useRouteMatch('/notes/:id')
当匹配到url时就能获取到对应的信息
参考资料: React-router hooks