react-router-dom 学习笔记
react-router-dom
react-router-dom 是 React 的实现路由功能的组件。
依赖安装
npm install react-router-dom --save
功能演示
路由和跳转
codesandbox 地址:https://codesandbox.io/s/react-router-basic-bnpsd?from-embed
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
// 下面一共有3个页面, 所有页面都是又浏览器动态的渲染, 而不是服务端渲染
// 尽管页面没有刷新, 但是 React Router 会保证路由URL和你点击的目标地址保持一致
// 它维护了浏览器的历史状态, 保证了书签或者返回按钮能够正常使用
// 下面的例子用到了 react-router-dom 组件的3个标签
// 1. Switch 保证只渲染一个子路由组件
// 2. Route 用于配置路由
// 3. Link 用于路由跳转
export default function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
</ul>
<hr />
{/*这里使用了 Switch, 是为了保证当多个路由都匹配成功时, 只渲染第一个匹配到的路由组件*/}
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/dashboard">
<Dashboard />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function About() {
return (
<div>
<h2>About</h2>
</div>
);
}
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
</div>
);
}
路由参数
codesandbox 地址:https://codesandbox.io/s/react-router-url-parameters-rrbsb?from-embed=&file=/example.js
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useParams
} from "react-router-dom";
export default function ParamsExample() {
return (
<Router>
<div>
<h2>Accounts</h2>
<ul>
<li>
<Link to="/netflix">Netflix</Link>
</li>
<li>
<Link to="/zillow-group">Zillow Group</Link>
</li>
<li>
<Link to="/yahoo">Yahoo</Link>
</li>
<li>
<Link to="/modus-create">Modus Create</Link>
</li>
</ul>
<Switch>
<Route path="/:id" children={<Child />} />
</Switch>
</div>
</Router>
);
}
function Child() {
// 这里使用了 react hooks
let { id } = useParams();
return (
<div>
<h3>ID: {id}</h3>
</div>
);
}
查询参数
import React from "react";
import {
BrowserRouter as Router,
Link,
useLocation
} from "react-router-dom";
// React Router does not have any opinions about
// how you should parse URL query strings.
//
// If you use simple key=value query strings and
// you do not need to support IE 11, you can use
// the browser's built-in URLSearchParams API.
//
// If your query strings contain array or object
// syntax, you'll probably need to bring your own
// query parsing function.
export default function QueryParamsExample() {
return (
<Router>
<QueryParamsDemo />
</Router>
);
}
// A custom hook that builds on useLocation to parse
// the query string for you.
function useQuery() {
return new URLSearchParams(useLocation().search);
}
function QueryParamsDemo() {
let query = useQuery();
return (
<div>
<div>
<h2>Accounts</h2>
<ul>
<li>
<Link to="/account?name=netflix">Netflix</Link>
</li>
<li>
<Link to="/account?name=zillow-group">Zillow Group</Link>
</li>
<li>
<Link to="/account?name=yahoo">Yahoo</Link>
</li>
<li>
<Link to="/account?name=modus-create">Modus Create</Link>
</li>
</ul>
<Child name={query.get("name")} />
</div>
</div>
);
}
function Child({ name }) {
return (
<div>
{name ? (
<h3>
The <code>name</code> in the query string is "{name}
"
</h3>
) : (
<h3>There is no name in the query string</h3>
)}
</div>
);
}
嵌套路由
体验地址:https://reactrouter.com/web/example/nesting
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useParams,
useRouteMatch
} from "react-router-dom";
export default function NestingExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/topics">
<Topics />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function Topics() {
/**
* 在 NestingExample 中我们是这样使用 Topics 组件的:
* <Route path="/topics">
* <Topics />
* </Route>
* 使用 useRouteMatch 时就能找到 Route 组件 <Route path="/topics"></Route>
*/
let { path, url } = useRouteMatch();
return (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${url}/rendering`}>Rendering with React</Link>
</li>
<li>
<Link to={`${url}/components`}>Components</Link>
</li>
<li>
<Link to={`${url}/props-v-state`}>Props v. State</Link>
</li>
</ul>
<Switch>
<Route exact path={path}>
<h3>Please select a topic.</h3>
</Route>
<Route path={`${path}/:topicId`}>
<Topic />
</Route>
</Switch>
</div>
);
}
function Topic() {
let { topicId } = useParams();
return (
<div>
<h3>{topicId}</h3>
</div>
);
}
路由重定向
codesandbox(这个官方例子有一个BUG): https://reactrouter.com/web/example/auth-workflow,路由重定向在实现权限管理是很有用的。
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
useHistory,
useLocation
} from "react-router-dom";
// 1. 这个例子有3个页面:公开页面, 受保护页面(登录后才能访问), 登录页面
// 2. 如果未登录访问受保护页面, 会重定向到登录页面, 登录之后重定向到受保护页面
// Notice the URL change each time. If you click the back
// button at this point, would you expect to go back to the
// login page? No! You're already logged in. Try it out,
// and you'll see you go back to the page you visited
// just *before* logging in, the public page.
export default function AuthExample() {
return (
<Router>
<div>
<AuthButton />
<ul>
<li>
<Link to="/public">Public Page</Link>
</li>
<li>
<Link to="/protected">Protected Page</Link>
</li>
</ul>
<Switch>
<Route path="/public">
<PublicPage />
</Route>
<Route path="/login">
<LoginPage />
</Route>
<PrivateRoute path="/protected">
<ProtectedPage />
</PrivateRoute>
</Switch>
</div>
</Router>
);
}
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
fakeAuth.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
fakeAuth.isAuthenticated = false;
setTimeout(cb, 100);
}
};
function AuthButton() {
let history = useHistory();
console.log(fakeAuth)
return fakeAuth.isAuthenticated ? (
<p>
Welcome!{" "}
<button
onClick={() => {
fakeAuth.signout(() => history.push("/"));
}}
>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
);
}
// A wrapper for <Route> that redirects to the login
// screen if you're not yet authenticated.
function PrivateRoute({ children, ...rest }) {
return (
<Route
{...rest}
render={({ location }) =>
fakeAuth.isAuthenticated ? (
children
) : (
<Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>
)
}
/>
);
}
function PublicPage() {
return <h3>Public</h3>;
}
function ProtectedPage() {
return <h3>Protected</h3>;
}
function LoginPage() {
let history = useHistory();
let location = useLocation();
let { from } = location.state || { from: { pathname: "/" } };
let login = () => {
fakeAuth.authenticate(() => {
history.replace(from);
});
};
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={login}>Log in</button>
</div>
);
}
自定义 Link
codesandbox: https://reactrouter.com/web/example/custom-link,思路是使用高阶组件来实现。
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useRouteMatch
} from "react-router-dom";
// This example show how you could create a custom
// <Link> that renders something special when the URL
// is the same as the one the <Link> points to.
export default function CustomLinkExample() {
return (
<Router>
<div>
<OldSchoolMenuLink
activeOnlyWhenExact={true}
to="/"
label="Home"
/>
<OldSchoolMenuLink to="/about" label="About" />
<hr />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
</Switch>
</div>
</Router>
);
}
function OldSchoolMenuLink({ label, to, activeOnlyWhenExact }) {
let match = useRouteMatch({
path: to,
exact: activeOnlyWhenExact
});
return (
<div className={match ? "active" : ""}>
{match && "> "}
<Link to={to}>{label}</Link>
</div>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function About() {
return (
<div>
<h2>About</h2>
</div>
);
}
阻止页面跳转
codesandbox: https://reactrouter.com/web/example/preventing-transitions,这个功能非常常见,如果你使用过 vue-router,那么你应该熟悉它关于导航守卫的一些方法。在 react-router-dom 中,主要依靠 Prompt 来实现这个功能,默认是一个非常丑的 prompt 弹窗,当然您可以配置(通过BrowserRouter或HashRouter配置属性getUserConfirmation)
import React, { useState } from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Prompt
} from "react-router-dom";
export default function PreventingTransitionsExample() {
return (
<Router>
<ul>
<li>
<Link to="/">Form</Link>
</li>
<li>
<Link to="/one">One</Link>
</li>
<li>
<Link to="/two">Two</Link>
</li>
</ul>
<Switch>
<Route path="/" exact children={<BlockingForm />} />
<Route path="/one" children={<h3>One</h3>} />
<Route path="/two" children={<h3>Two</h3>} />
</Switch>
</Router>
);
}
function BlockingForm() {
let [isBlocking, setIsBlocking] = useState(false);
return (
<form
onSubmit={event => {
event.preventDefault();
event.target.reset();
setIsBlocking(false);
}}
>
<Prompt
when={isBlocking}
message={location =>
`Are you sure you want to go to ${location.pathname}`
}
/>
<p>
Blocking?{" "}
{isBlocking ? "Yes, click a link or the back button" : "Nope"}
</p>
<p>
<input
size="50"
placeholder="type something to block transitions"
onChange={event => {
setIsBlocking(event.target.value.length > 0);
}}
/>
</p>
<p>
<button>Submit to stop blocking</button>
</p>
</form>
);
}
404 页面配置
import React from "react";
import {
BrowserRouter as Router,
Route,
Link,
Switch,
Redirect,
useLocation
} from "react-router-dom";
export default function NoMatchExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/old-match">Old Match, to be redirected</Link>
</li>
<li>
<Link to="/will-match">Will Match</Link>
</li>
<li>
<Link to="/will-not-match">Will Not Match</Link>
</li>
<li>
<Link to="/also/will/not/match">Also Will Not Match</Link>
</li>
</ul>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/old-match">
<Redirect to="/will-match" />
</Route>
<Route path="/will-match">
<WillMatch />
</Route>
<Route path="*">
<NoMatch />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return <h3>Home</h3>;
}
function WillMatch() {
return <h3>Matched!</h3>;
}
function NoMatch() {
let location = useLocation();
return (
<div>
<h3>
No match for <code>{location.pathname}</code>
</h3>
</div>
);
}
递归路由
codesandbox: https://reactrouter.com/web/example/recursive-paths,这个例子非常有趣,玩一玩才能体会
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
useParams,
useRouteMatch
} from "react-router-dom";
// Sometimes you don't know all the possible routes
// for your application up front; for example, when
// building a file-system browsing UI or determining
// URLs dynamically based on data. In these situations,
// it helps to have a dynamic router that is able
// to generate routes as needed at runtime.
//
// This example lets you drill down into a friends
// list recursively, viewing each user's friend list
// along the way. As you drill down, notice each segment
// being added to the URL. You can copy/paste this link
// to someone else and they will see the same UI.
//
// Then click the back button and watch the last
// segment of the URL disappear along with the last
// friend list.
export default function RecursiveExample() {
return (
<Router>
<Switch>
<Route path="/:id">
<Person />
</Route>
<Route path="/">
<Redirect to="/0" />
</Route>
</Switch>
</Router>
);
}
function Person() {
let { url } = useRouteMatch();
let { id } = useParams();
let person = find(parseInt(id));
return (
<div>
<h3>{person.name}’s Friends</h3>
<ul>
{person.friends.map(id => (
<li key={id}>
<Link to={`${url}/${id}`}>{find(id).name}</Link>
</li>
))}
</ul>
<Switch>
<Route path={`${url}/:id`}>
<Person />
</Route>
</Switch>
</div>
);
}
const PEEPS = [
{ id: 0, name: "Michelle", friends: [1, 2, 3] },
{ id: 1, name: "Sean", friends: [0, 3] },
{ id: 2, name: "Kim", friends: [0, 1, 3] },
{ id: 3, name: "David", friends: [1, 2] }
];
function find(id) {
return PEEPS.find(p => p.id === id);
}
导航菜单配置
https://reactrouter.com/web/example/sidebar 这个例子的参考意义不大,实际需求中菜单往往也是通过 Route 配置计算而得。
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
// Each logical "route" has two components, one for
// the sidebar and one for the main area. We want to
// render both of them in different places when the
// path matches the current URL.
// We are going to use this route config in 2
// spots: once for the sidebar and once in the main
// content section. All routes are in the same
// order they would appear in a <Switch>.
const routes = [
{
path: "/",
exact: true,
sidebar: () => <div>home!</div>,
main: () => <h2>Home</h2>
},
{
path: "/bubblegum",
sidebar: () => <div>bubblegum!</div>,
main: () => <h2>Bubblegum</h2>
},
{
path: "/shoelaces",
sidebar: () => <div>shoelaces!</div>,
main: () => <h2>Shoelaces</h2>
}
];
export default function SidebarExample() {
return (
<Router>
<div style={{ display: "flex" }}>
<div
style={{
padding: "10px",
width: "40%",
background: "#f0f0f0"
}}
>
<ul style={{ listStyleType: "none", padding: 0 }}>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/bubblegum">Bubblegum</Link>
</li>
<li>
<Link to="/shoelaces">Shoelaces</Link>
</li>
</ul>
<Switch>
{routes.map((route, index) => (
// You can render a <Route> in as many places
// as you want in your app. It will render along
// with any other <Route>s that also match the URL.
// So, a sidebar or breadcrumbs or anything else
// that requires you to render multiple things
// in multiple places at the same URL is nothing
// more than multiple <Route>s.
<Route
key={index}
path={route.path}
exact={route.exact}
children={<route.sidebar />}
/>
))}
</Switch>
</div>
<div style={{ flex: 1, padding: "10px" }}>
<Switch>
{routes.map((route, index) => (
// Render more <Route>s with the same paths as
// above, but different components this time.
<Route
key={index}
path={route.path}
exact={route.exact}
children={<route.main />}
/>
))}
</Switch>
</div>
</div>
</Router>
);
}
路由过渡效果
https://reactrouter.com/web/example/animated-transitions
import "./packages/react-router-dom/examples/Animation/styles.css";
import React from "react";
import {
TransitionGroup,
CSSTransition
} from "react-transition-group";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
useLocation,
useParams
} from "react-router-dom";
export default function AnimationExample() {
return (
<Router>
<Switch>
<Route exact path="/">
<Redirect to="/hsl/10/90/50" />
</Route>
<Route path="*">
<AnimationApp />
</Route>
</Switch>
</Router>
);
}
function AnimationApp() {
let location = useLocation();
return (
<div style={styles.fill}>
<ul style={styles.nav}>
<NavLink to="/hsl/10/90/50">Red</NavLink>
<NavLink to="/hsl/120/100/40">Green</NavLink>
<NavLink to="/rgb/33/150/243">Blue</NavLink>
<NavLink to="/rgb/240/98/146">Pink</NavLink>
</ul>
<div style={styles.content}>
<TransitionGroup>
{/*
This is no different than other usage of
<CSSTransition>, just make sure to pass
`location` to `Switch` so it can match
the old location as it animates out.
*/}
<CSSTransition
key={location.key}
classNames="fade"
timeout={300}
>
<Switch location={location}>
<Route path="/hsl/:h/:s/:l" children={<HSL />} />
<Route path="/rgb/:r/:g/:b" children={<RGB />} />
</Switch>
</CSSTransition>
</TransitionGroup>
</div>
</div>
);
}
function NavLink(props) {
return (
<li style={styles.navItem}>
<Link {...props} style={{ color: "inherit" }} />
</li>
);
}
function HSL() {
let { h, s, l } = useParams();
return (
<div
style={{
...styles.fill,
...styles.hsl,
background: `hsl(${h}, ${s}%, ${l}%)`
}}
>
hsl({h}, {s}%, {l}%)
</div>
);
}
function RGB() {
let { r, g, b } = useParams();
return (
<div
style={{
...styles.fill,
...styles.rgb,
background: `rgb(${r}, ${g}, ${b})`
}}
>
rgb({r}, {g}, {b})
</div>
);
}
const styles = {};
styles.fill = {
position: "absolute",
left: 0,
right: 0,
top: 0,
bottom: 0
};
styles.content = {
...styles.fill,
top: "40px",
textAlign: "center"
};
styles.nav = {
padding: 0,
margin: 0,
position: "absolute",
top: 0,
height: "40px",
width: "100%",
display: "flex"
};
styles.navItem = {
textAlign: "center",
flex: 1,
listStyleType: "none",
padding: "10px"
};
styles.hsl = {
...styles.fill,
color: "white",
paddingTop: "20px",
fontSize: "30px"
};
styles.rgb = {
...styles.fill,
color: "white",
paddingTop: "20px",
fontSize: "30px"
};
路由集中管理
这个例子演示了,如何做到路由集中管理,真实的项目中,导航菜单的逻辑往往也是和一起路由集中管理的。
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
// Some folks find value in a centralized route config.
// A route config is just data. React is great at mapping
// data into components, and <Route> is a component.
// Our route config is just an array of logical "routes"
// with `path` and `component` props, ordered the same
// way you'd do inside a `<Switch>`.
const routes = [
{
path: "/sandwiches",
component: Sandwiches
},
{
path: "/tacos",
component: Tacos,
routes: [
{
path: "/tacos/bus",
component: Bus
},
{
path: "/tacos/cart",
component: Cart
}
]
}
];
export default function RouteConfigExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/tacos">Tacos</Link>
</li>
<li>
<Link to="/sandwiches">Sandwiches</Link>
</li>
</ul>
<Switch>
{routes.map((route, i) => (
<RouteWithSubRoutes key={i} {...route} />
))}
</Switch>
</div>
</Router>
);
}
// A special wrapper for <Route> that knows how to
// handle "sub"-routes by passing them in a `routes`
// prop to the component it renders.
function RouteWithSubRoutes(route) {
return (
<Route
path={route.path}
render={props => (
// pass the sub-routes down to keep nesting
<route.component {...props} routes={route.routes} />
)}
/>
);
}
function Sandwiches() {
return <h2>Sandwiches</h2>;
}
function Tacos({ routes }) {
return (
<div>
<h2>Tacos</h2>
<ul>
<li>
<Link to="/tacos/bus">Bus</Link>
</li>
<li>
<Link to="/tacos/cart">Cart</Link>
</li>
</ul>
<Switch>
{routes.map((route, i) => (
<RouteWithSubRoutes key={i} {...route} />
))}
</Switch>
</div>
);
}
function Bus() {
return <h3>Bus</h3>;
}
function Cart() {
return <h3>Cart</h3>;
}
API
useParams hooks
useParams
可以帮助我们在各层组件中,轻松访问 router 的 params 参数。
// V5.1以前
const Detail = (props) => {
const { match: { params } } = props
const { id } = params
return (
<div>
params id: { id }
<DetailTips/>
</div>
)
}
// 需要使用高阶组件 withRouter
const DetailTips = withRouter((props) => {
const { match: { params } } = props
const { id } = params
return (
<div>params id: { id }</div>
)
})
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route path="/detail/:id" component={Detail}/>
</Switch>
</Router>
</div>
);
}
V5.1以后,由于 useParams 的引入,我们可以轻松获取路由参数。对于更深层的组件,也不需要借助高阶组件withRouter,帮助我们拿到路由参数。
const Detail = () => {
const { id } = useParams()
return (
<div>
params id: { id }
<DetailTips/>
</div>
)
}
// 不需要使用高阶组件 withRouter
const DetailTips = () => {
const { id } = useParams()
return (
<div>params id: { id }</div>
)
}
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route path="/detail/:id" component={Detail}/>
</Switch>
</Router>
</div>
)
}
useLocation hooks
useLocation 可以帮助我们在各层组件中,轻松获取 location 对象。在V5.1版本之前,我们需要使用props.location。而对于更深层的组件,还需要使用 withRouter。
V5.1以前:
const Detail = (props) => {
const { location: { pathname } } = props
return (
<div>
pathname: { pathname }
<DetailTips/>
</div>
)
}
// 需要使用高阶组件 withRouter
const DetailTips = withRouter((props) => {
const { location: { pathname } } = props
return (
<div>pathname: { pathname }</div>
)
})
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route path="/detail/:id" component={Detail}/>
</Switch>
</Router>
</div>
);
}
V5.1以后:
const Detail = (props) => {
const { pathname } = useLocation()
return (
<div>
pathname: { pathname }
<DetailTips/>
</div>
)
}
// 不需要使用高阶组件 withRouter
const DetailTips = (props) => {
const { pathname } = useLocation()
return (
<div>pathname: { pathname }</div>
)
}
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route path="/detail/:id" component={Detail}/>
</Switch>
</Router>
</div>
);
}
useHistory hooks
useHistory可以帮助我们访问history对象,进行编程式的导航。
const Home = () => {
return (
<div>Home</div>
)
}
const Detail = () => {
const history = useHistory()
return (
<div>
<button onClick={() => { history.push('/')}}>go home</button>
</div>
)
}
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/detail/:id" component={Detail}/>
</Switch>
</Router>
</div>
);
}
useRouteMatch hooks
useRouteMatch,接受一个path字符串作为参数。当参数的path与当前的路径相匹配时,useRouteMatch会返回match对象,否则返回null。
useRouteMatch在对于一些,不是路由级别的组件。但是组件自身的显隐却和当前路径相关的组件时,非常有用。比如,你在做一个后台管理系统时,网页的Header只会在登录页显示,登录完成后不需要显示,这种场景下就可以用到useRouteMatch
V5.0以前:
const Home = () => {
return (
<div>Home</div>
)
}
// Header组件只会在匹配`/detail/:id`时出现
const Header = () => {
return (
<Route
path="/detail/:id"
strict
sensitive
render={({ match }) => {
return match && <div>Header</div>
}}
/>
)
}
const Detail = () => {
return (
<div>Detail</div>
)
}
function App() {
return (
<div className="App">
<Router>
<Header/>
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/detail/:id" component={Detail}/>
</Switch>
</Router>
</div>
);
}
V5.1以后:
const Home = () => {
return (
<div>Home</div>
)
}
// Header组件只会在匹配`/detail/:id`时出现
const Header = () => {
const match = useRouteMatch('/detail/:id')
return (
match && <div>Header</div>
)
}
const Detail = () => {
return (
<div>Detail</div>
)
}
function App() {
return (
<div className="App">
<Router>
<Header/>
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/detail/:id" component={Detail}/>
</Switch>
</Router>
</div>
);
}
BrowserRouter
进阶
路由配置
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
function Home() {
return (
<div>
<div>Home</div>
</div>
)
}
function About() {
return (
<div>
<div>About</div>
</div>
)
}
function Product() {
return (
<div>
<div>Product</div>
</div>
)
}
function Layout(props) {
return (
<div>
<div>layout</div>
{props.children}
</div>
)
}
const routes = [
{
path: "/",
component: Layout,
children: [
{
path: "/home",
title: '工作台',
exact: true,
component: Home
},
{
path: "/about",
title: '关于',
exact: true,
component: About
},
{
path: "/product",
title: '商品',
component: Product,
}
]
}
];
export default function SidebarExample() {
return (
<Router>
<div style={{ display: "flex" }}>
<div
style={{
padding: "10px",
width: "40%",
background: "#f0f0f0",
display: 'flex'
}}
>
<ul style={{ listStyleType: "none", padding: 0 }}>
{
routes.map(route => {
return (
<li key={route.title}>
<Link to={route.path}>{route.title}</Link>
</li>
)
})
}
</ul>
<Switch>
{routes.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
children={<route.component />}
/>
))}
</Switch>
</div>
</div>
</Router>
);
}
未完待续...