老牌的应用获取新页面需要发送HTTP请求,由服务器返回HTML
在单页面应用(single page apps)中,我们实际总是在同一页面, 如果发送HTTP GET请求,也只是为了获取JSON数据
每个视图(view)最好都有自己的地址,这样方便用户收藏书签,浏览器返回到上一页面也得以实现

安装React-router-dom

  1. npm install react-router-dom

一个使用React-router的示例

  1. import React from 'react'
  2. import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'
  3. const Home = () => (
  4. <div>
  5. {' '}
  6. <h2>TKTL notes app</h2>{' '}
  7. </div>
  8. )
  9. const Notes = () => (
  10. <div>
  11. {' '}
  12. <h2>Notes</h2>{' '}
  13. </div>
  14. )
  15. const Users = () => (
  16. <div>
  17. {' '}
  18. <h2>Users</h2>{' '}
  19. </div>
  20. )
  21. const App = () => {
  22. const padding = {
  23. padding: 5,
  24. }
  25. return (
  26. <Router>
  27. <div>
  28. <Link style={padding} to="/">
  29. home
  30. </Link>
  31. <Link style={padding} to="/notes">
  32. notes
  33. </Link>
  34. <Link style={padding} to="/users">
  35. users
  36. </Link>
  37. </div>
  38. <Switch>
  39. <Route path="/notes">
  40. <Notes />
  41. </Route>
  42. <Route path="/users">
  43. <Users />
  44. </Route>
  45. <Route path="/">
  46. <Home />
  47. </Route>
  48. </Switch>
  49. <div>
  50. <i>Note app, Department of Computer Science 2021</i>
  51. </div>
  52. </Router>
  53. )
  54. }
  55. export default App

路由,或者基于url的条件渲染,通过将组件放在Router组件中来实现
Router实际上是导入BrowserRouter后的别称

  1. import { BrowserRouter as Router,
  2. 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

  1. <Link to="/notes">notes</Link>

根据地址栏URL来渲染对应的组件,是在Route组件中实现的

  1. <Route path="/notes">
  2. <Notes />
  3. </Route>

将所有基于url渲染的组件都放在Switch组件里, 即这些组件的视图区域都在Switch里

  1. <Switch>
  2. <Route path="/notes">
  3. <Notes />
  4. </Route>
  5. <Route path="/users">
  6. <Users />
  7. </Route>
  8. <Route path="/">
  9. <Home />
  10. </Route>
  11. </Switch>

注意,在Switch内部的顺序很重要,如果将path=”/“放在第一个,所有以’/‘开头的url都能与之匹配,导致其他所有组件都得不到渲染。
同理,如果url A包含了B, 且A比B长,那么A要放前面,否则永远匹配不到A

  1. <Switch>
  2. <Route path={'/anecdotes/:id'}>
  3. <Anecdote anecdote={anecdote} />
  4. </Route>
  5. <Route path={'/anecdotes'}>
  6. <AnecdoteList anecdotes={anecdotes} />
  7. </Route>
  8. </Switch>

Parameterized route

每条li都是可以点击的Link, 当点击时,修改地址栏的url

  1. const Notes = ({notes}) => (
  2. <div>
  3. <h2>Notes</h2>
  4. <ul>
  5. {notes.map(note =>
  6. <li key={note.id}>
  7. <Link to={`/notes/${note.id}`}>{note.content}</Link>
  8. </li>
  9. )}
  10. </ul>
  11. </div>
  12. )

Route通过**:id**传递参数(express style)

  1. <Switch>
  2. <Route path="/notes/:id">
  3. <Note notes={notes} />
  4. </Route>
  5. <Route path="/notes">
  6. <Notes notes={notes} />
  7. </Route>
  8. <Route path="/">
  9. <Home />
  10. </Route>
  11. </Switch>

通过**useParams()**获取url的参数

  1. import {
  2. // ...
  3. useParams
  4. } from "react-router-dom"
  5. const Note = ({ notes }) => {
  6. const id = useParams().id
  7. const note = notes.find(n => n.id === Number(id))
  8. return (
  9. <div>
  10. <h2>{note.content}</h2>
  11. <div>{note.user}</div>
  12. <div><strong>{note.important ? 'important' : ''}</strong></div>
  13. </div>
  14. )
  15. }

useHistory

通过useHistory, 组件可以访问history对象,通过向history对象push一个地址,将url修改为对应的地址并渲染对应的组件。

  1. import {
  2. // ...
  3. useHistory
  4. } from 'react-router-dom'
  5. const Login = (props) => {
  6. const history = useHistory()
  7. const onSubmit = (event) => {
  8. event.preventDefault()
  9. props.onLogin('mluukkai')
  10. history.push('/')
  11. }
  12. return (
  13. <div>
  14. <h2>login</h2>
  15. <form onSubmit={onSubmit}>
  16. <div>
  17. username: <input />
  18. </div>
  19. <div>
  20. password: <input type='password' />
  21. </div>
  22. <button type="submit">login</button>
  23. </form>
  24. </div>
  25. )
  26. }

Redirect

如果user不存在,则不渲染Users组件,而是重定向到/login, 即修改地址为/login并渲染

  1. <Route path="/users">
  2. {user ? <Users /> : <Redirect to="/login" />}
  3. </Route>

Parameterized route revisited

使用useRouteMatch获取匹配的url信息
将Router移到App外面

  1. ReactDOM.render(
  2. <Router>
  3. <App />
  4. </Router>,
  5. document.getElementById('root')
  6. )

在App组件内部使用useRouteMatch

  1. import {
  2. // ...
  3. useRouteMatch
  4. } from "react-router-dom"
  5. const App = () => {
  6. // ...
  7. const match = useRouteMatch('/notes/:id')
  8. const note = match
  9. ? notes.find(note => note.id === Number(match.params.id))
  10. : null
  11. return (
  12. <div>
  13. <div>
  14. <Link style={padding} to="/">home</Link>
  15. // ...
  16. </div>
  17. <Switch>
  18. <Route path="/notes/:id">
  19. <Note note={note} />
  20. </Route>
  21. // ...
  22. </Switch>
  23. </div>
  24. )
  25. }

每次浏览器url更改时,都会执行如下代码

  1. const match = useRouteMatch('/notes/:id')

当匹配到url时就能获取到对应的信息

参考资料: React-router hooks