36

HOOKS

React Router附带了一些HOOK,可让您访问路由器的状态并从组件内部执行导航

  • useHistory
  • useLocation
  • useParams
  • useRouteMatch

useHistory

useHistory 钩子返回 history 对象,可以使用 useHistory 进行导航

import { useHistory } from "react-router-dom";

function HomeButton() {
  let history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}

useLocation

useLocation 钩子返回当前URL的 location 对象。可以把它想象成一个useState,每当URL发生变化时,它都会返回一个新的位置。
这是非常有用的,例如,当一个新的页面加载时,比如使用web分析工具触发一个新的“页面视图”事件,如下面的例子所示:

import React from "react";
import ReactDOM from "react-dom";
import {
  BrowserRouter as Router,
  Switch,
  useLocation
} from "react-router-dom";

function usePageViews() {
  let location = useLocation();
  React.useEffect(() => {
    ga.send(["pageview", location.pathname]);
  }, [location]);
}

function App() {
  usePageViews();
  return <Switch>...</Switch>;
}

ReactDOM.render(
  <Router>
    <App />
  </Router>,
  node
);

useParams

useParams 动态参数列表的引用对象,用于获取<Route>中的 match.params (动态参数)

import React from "react";
import ReactDOM from "react-dom";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  useParams
} from "react-router-dom";

function BlogPost() {
  let { slug } = useParams();
  return <div>Now showing post {slug}</div>;
}

ReactDOM.render(
  <Router>
    <Switch>
      <Route exact path="/">
        <HomePage />
      </Route>
      <Route path="/blog/:slug">
        <BlogPost />
      </Route>
    </Switch>
  </Router>,
  node
);

useRouteMatch

useRouteMatch 尝试以与<Route>相同的方式匹配当前URL。它主要用于访问匹配数据,而无需实际渲染<Route>

Now, instead of

import { Route } from "react-router-dom";

function BlogPost() {
  return (
    <Route
      path="/blog/:slug"
      render={({ match }) => {
        // Do whatever you want with the match...
        return <div />;
      }}
    />
  );
}

you can just

import { useRouteMatch } from "react-router-dom";

function BlogPost() {
  let match = useRouteMatch("/blog/:slug");

  // Do whatever you want with the match...
  return <div />;
}

useRouteMatch 钩子也可以:

  • 不接受任何参数并返回当前<Route>
  • 接受单个参数,与matchPath的props参数相同。它可以是一个字符串形式的路径名(就像上面的例子),也可以是一个带有匹配道具的对象,像这样:
const match = useRouteMatch({
  path: "/BLOG/:slug/",
  strict: true,
  sensitive: true
});

<BrowserRouter>

Router使用HTML5 History API((pushState, replaceState and the popstate event)来保证 UI 组件和 URL 同步

<BrowserRouter
  basename={optionalString}
  forceRefresh={optionalBool}
  getUserConfirmation={optionalFunc}
  keyLength={optionalNumber}
>
  <App />
</BrowserRouter>

basename <string>
基准URL,如果你的应用程序是从服务器上的子目录中提供的,你需要将其设置为子目录。一个正确格式化的basename应该有一个开头斜杠,但没有结尾斜杠。

<BrowserRouter basename="/calendar">
    <Link to="/today"/> // renders <a href="/calendar/today">
    <Link to="/tomorrow"/> // renders <a href="/calendar/tomorrow">
    ...
</BrowserRouter>

getUserConfirmation <function>
用于确认路由跳转的函数。默认使用window.confirm

<BrowserRouter
  getUserConfirmation={(message, callback) => {
    // this is the default behavior
    const allowTransition = window.confirm(message);
    callback(allowTransition);
  }}
/>

forceRefresh <boolean>
如果值为 true 路由跳转将会刷新整个页面。可以使用它来模拟传统服务器渲染应用程序,路由跳转之间刷新页面的工作方式

<BrowserRouter forceRefresh={true} />

keyLength <number>
location.key的长度。默认为6。

<BrowserRouter keyLength={12} />

children <element>
要渲染的子元素。
Note: 如果React < 16,必须使用单个子元素,因为render方法不能返回多个元素。如果您需要多个元素,则可以尝试将它们包装在extra中<div>

<HashRouter>

<Router>使用 Hash 模式路由(i.e. window.location.hash)来保证 UI 组件和 URL 同步
重要说明:Hash模式不支持location.key或location.state。在以前的版本中,我们尝试对行为进行兼容处理,但存在无法解决的极端情况。任何需要此行为的代码或插件都将无法工作。由于此技术仅旨在支持旧版浏览器,因此我们建议您配置服务器以供使用<BrowserHistory>

<HashRouter
  basename={optionalString}
  getUserConfirmation={optionalFunc}
  hashType={optionalString}
>
  <App />
</HashRouter>

basename <string>
基准URL,如果你的应用程序是从服务器上的子目录中提供的,你需要将其设置为子目录。一个正确格式化的basename应该有一个开头斜杠,但没有结尾斜杠。

<HashRouter basename="/calendar"/>
<Link to="/today"/> // renders <a href="#/calendar/today">

getUserConfirmation <function>
用于确认路由跳转的函数。默认使用window.confirm

<HashRouter
  getUserConfirmation={(message, callback) => {
    // this is the default behavior
    const allowTransition = window.confirm(message);
    callback(allowTransition);
  }}
/>

hashType <string>
window.location.hash 的编码类型。可用值:

  • "slash" - 类似 #/ 或 #/sunshine/lollipops
  • "noslash" - 类似 # 或 #sunshine/lollipops
  • "hashbang" - 创建 “ajax crawlable”Hash (Google弃用) 类似 #!/ 或 #!/sunshine/lollipops
    默认为"slash"。
    children <element>
    渲染单个子元素

<Link>

提供声明式、可访问的导航。

<Link to="/about">About</Link>

to <string>
链接路径的字符串形式,由路由路径、搜索参数和 Hash 属性构成

<Link to="/courses?sort=name" />

to <object>
具有以下属性:

  • pathname: <string> 表示要链接到的路径
  • search: <string> 查询参数
  • hash: 一个放在URL中的Hash,例如 #a-hash.
  • state: location.state 参数.

to <function>

将当前路由信息作为参数传递,该函数返回string或者object
<Link to={location => ({ ...location, pathname: "/courses" })} />
<Link to={location => `${location.pathname}?sort=name`} />

replace <boolean>
如果为 true,单击链接时将替换当前历史条目,而不是添加新条目

<Link to="/courses" replace />

innerRef <RefObject>
从React Router 5.1开始,如果您使用的是React 16,则不需要此功能,因为我们会将ref转发到底层<a>.使用普通的ref代替
使用React.createRef获取组件的底层引用。
component <React.Component>
如果需要使用自定义的导航组件,可以通过 component 指定

const FancyLink = React.forwardRef((props, ref) => (
  <a ref={ref} {...props}>💅 {props.children}</a>
))

<Link to="/" component={FancyLink} />

others
可以传递需要传递的参数,如title, id, className等。

<NavLink>

特殊的<Link>,当它匹配当前URL时,它会为当前处于激活状态链接添加样式。

<NavLink to="/about">About</NavLink>

activeClassName <string>
元素处于活动状态时提供的class样式。默认的类名称是active。这将与className 连接

<NavLink to="/faq" activeClassName="selected">
  FAQs
</NavLink>

activeStyle <object>
当元素处于活动状态时应用于元素的内联style样式。

<NavLink
  to="/faq"
  activeStyle={{
    fontWeight: "bold",
    color: "red"
  }}
>
  FAQs
</NavLink>

strict <boolean>
如果为true,URL匹配时使用严格模式,路径的末尾斜杠也会匹配。更多信息请参阅<Route strict>文档

isActive <function>
用于添加额外的逻辑以确定链接是否活动状态。如果您想要做的不仅仅是验证链接的路径名与当前URL的路径名是否匹配,那么就应该使用这个方法。

<NavLink
  to="/events/123"
  isActive={(match, location) => {
    if (!match) {
      return false;
    }

    // only consider an event active if its event id is an odd number
    const eventID = parseInt(match.params.eventID);
    return !isNaN(eventID) && eventID % 2 === 1;
  }}
>
  Event 123
</NavLink>

location <object>
isActive一般用于比较当前的历史位置(通常是当前浏览器的URL)。为了与不同的位置进行比较,可以传递一个location

aria-current <string>
在活动链接上使用的 aria-current 属性的值。可用值:

  • "page" - 用于指定一组分页链接中的链接
  • "step" - 用于指示基于步骤的进程的步骤指示器中的链接
  • "location" - 用于指示在视觉上高亮显示为流程图的当前组件的图像
  • "date" - 用于指示日历中的当前日期
  • "time" - 用于指示时间表中的当前时间
  • "true" - 用于指示导航链接是否处于活动状态
  • "false" - 用于防止辅助技术对当前链接做出反应(用例是在一个页面上阻止多个aria-current标签)
    默认为 "page".
    基于 WAI-ARIA 1.1 specifications

<Prompt>

重新导出核心提示符

<MemoryRouter>

特殊<Router>,将URL的历史记录保存在内存中(不读取或写入地址栏)。在测试和非浏览器环境(如React Native)中很有用。

<MemoryRouter
  initialEntries={optionalArray}
  initialIndex={optionalNumber}
  getUserConfirmation={optionalFunc}
  keyLength={optionalNumber}
>
  <App />
</MemoryRouter>

initialEntries <array>
历史堆栈中的位置数组。这些对象可以是包含{pathname, search, hash, state}的完整位置对象,也可以是简单的字符串url。

<MemoryRouter
  initialEntries={["/one", "/two", { pathname: "/three" }]}
  initialIndex={1}
>
  <App />
</MemoryRouter>

initialIndex <number>
initialEntries 数组中初始位置的索引。

getUserConfirmation <function>
用于确认导航的函数。当直接使用<MemoryRouter>和<Prompt>时,必须使用这个选项。

keyLength <number>
location.key的长度。默认为6。

children <element>
要渲染的子元素
Note: 如果React < 16,必须使用单个子元素,因为render方法不能返回多个元素。如果您需要多个元素,则可以尝试将它们包装在extra中<div>

<Redirect>

<Redirect>会导航到新位置。新位置将覆盖历史记录堆栈中的当前位置,就像服务器端重定向(HTTP 3xx)一样

<Route exact path="/">
  {loggedIn ? <Redirect to="/dashboard" /> : <PublicHomePage />}
</Route>

to <string>
要重定向到的URL。任何可以被 path-to-regexp@^1.7.0 识别的有效URL路径,在to中使用的所有URL参数都必须由from覆盖

<Redirect to="/somewhere/else" />

to <object>
要重定向到的位置。pathname是可以被 path-to-regexp@^1.7.0 识别的有效URL路径。

<Redirect
  to={{
    pathname: "/login",
    search: "?utm=your+face",
    state: { referrer: currentLocation }
  }}
/>

state对象可以通过this.props.location访问。重定向到组件中的状态。这个新的引用键(它不是一个特殊的名称)然后将通过this.props.location.state访问。由路径名'/ Login '指向的Login组件中的referrer

push <boolean>

<Redirect push to="/somewhere/else" />

如果为true,重定向会将新的条目推入历史记录,而不是替换当前条目

from <string>
要重定向的路径名。任何可以被 path-to-regexp@^1.7.0 识别的有效URL路径。必须包含中使用的所有参数to。不使用的其他参数将to被忽略。

Note: 仅当渲染的<Redirect>内部时,才能用于匹配位置<Switch>。请参阅<Switch children>以获取更多详细信息。

<Switch>
  <Redirect from="/old-path" to="/new-path" />
  <Route path="/new-path">
    <Place />
  </Route>
</Switch>

// Redirect with matched parameters
<Switch>
  <Redirect from="/users/:id" to="/users/profile/:id" />
  <Route path="/users/profile/:id">
    <Profile />
  </Route>
</Switch>

exact <boolean>
完全匹配;相当于Route.exact

NOTE: 当在<Switch>中渲染<Redirect>时,这只能与from一起使用,以精确匹配位置。参见<Switch children>了解更多细节。

<Switch>
  <Redirect exact from="/" to="/home" />
  <Route path="/home">
    <Home />
  </Route>
  <Route path="/about">
    <About />
  </Route>
</Switch>

strict <boolean>
严格的匹配;相当于Route.strict。

Note: 当在<Switch>中渲染<Redirect>时,它只能与from一起使用,以严格匹配位置。参见<<Switch children>了解更多细节。

sensitive <boolean>
区分大小写的匹配;相当于Route.sensitive

<Route>

路由组件可能是React路由器中最重要的组件,需要理解和学习使用。它最基本的职责是在路径与当前URL匹配时渲染指定的UI组件。
想想以下代码:

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";

ReactDOM.render(
  <Router>
    <div>
      <Route exact path="/">
        <Home />
      </Route>
      <Route path="/news">
        <NewsFeed />
      </Route>
    </div>
  </Router>,
  node
);

如果应用程序的URL路径是 '/',那么UI层次结构将是这样的:

<div>
  <Home />
  <!-- react-empty: 2 -->
</div>

如果应用的位置是/news,那么UI层次结构将是:

<div>
  <!-- react-empty: 1 -->
  <NewsFeed />
</div>

“React -empty” 注释只是React的null渲染的实现细节。但就我们的目的而言,它是有启发意义的。一个路由在技术上总是被“渲染”的,即使它被渲染为null。当<Route>的路径与当前URL匹配时,它就会渲染它的子组件(你的组件)。

如果同一个组件在组件树的同一点被用作多个<Route>的子组件,React会将其视为同一个组件实例,并且在路由更改之间,组件的状态会被保留。如果不希望这样做,那么在路由发生变化时,给每个路由组件添加一个唯一的key,将导致React重新创建该组件实例。

Route渲染方法
使用<Route>来渲染内容的推荐方法是使用子元素,如上所示。不过,还有一些其他方法可以用来渲染<Route>。提供这些功能主要是为了在引入钩子之前使用早期版本的路由器构建应用程序。

Route属性
所有的<Route>的渲染方法,都会传递三个参数

component
<Route>匹配时渲染的 React 子组件。它将与route属性一起渲染

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";

// All route props (match, location and history) are available to User
function User(props) {
  return <h1>Hello {props.match.params.username}!</h1>;
}

ReactDOM.render(
  <Router>
    <Route path="/user/:username" component={User} />
  </Router>,
  node
);

当您使用component(而不是下面的render或children)时,路由器将使用React.createElement给定的组件创建一个新的React元素。这意味着,如果为component prop提供内联函数,则将在每个渲染器中创建一个新组件。这将导致现有组件的卸载和新组件的安装,而不仅仅是更新现有组件。当使用内联函数进行内联渲染时,请使用render或children道具(如下)。

render <function>
这样可以方便地进行内联渲染和包装,而无需进行上述不必要的重新安装。
而不是使用 component 创建一个新的React元素,传入路径匹配时调用的函数。render prop函数可以访问所有与render prop组件相同的路由道具(match, location , history)。

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";

// convenient inline rendering
ReactDOM.render(
  <Router>
    <Route path="/home" render={() => <div>Home</div>} />
  </Router>,
  node
);

// wrapping/composing
// You can spread routeProps to make them available to your rendered Component
function FadingRoute({ component: Component, ...rest }) {
  return (
    <Route
      {...rest}
      render={routeProps => (
        <FadeIn>
          <Component {...routeProps} />
        </FadeIn>
      )}
    />
  );
}

ReactDOM.render(
  <Router>
    <FadingRoute path="/cool" component={Something} />
  </Router>,
  node
);

Warning: <Route component>优先于<Route render>,所以不要在<Route>中同时使用。

children <function>
有时需要判断渲染路径是否与路由路径匹配。在这种情况下,您可以使用children prop属性。它的工作原理完全相同,render不管是否有匹配
children 与 component, render 接收相同的 match,location,history,除非路由不能匹配URL,否则match为空。允许根据路由是否匹配,来动态调整渲染UI组件。如果路由匹配,我们会添加一个活动类

import React from "react";
import ReactDOM from "react-dom";
import {
  BrowserRouter as Router,
  Link,
  Route
} from "react-router-dom";

function ListItemLink({ to, ...rest }) {
  return (
    <Route
      path={to}
      children={({ match }) => (
        <li className={match ? "active" : ""}>
          <Link to={to} {...rest} />
        </li>
      )}
    />
  );
}

ReactDOM.render(
  <Router>
    <ul>
      <ListItemLink to="/somewhere" />
      <ListItemLink to="/somewhere-else" />
    </ul>
  </Router>,
  node
);

对于动画也很有用:

<Route
  children={({ match, ...rest }) => (
    {/* Animate will always render, so you can use lifecycles
        to animate its child in and out */}
    <Animate>
      {match && <Something {...rest}/>}
    </Animate>
  )}
/>

Warning: <Route children>优先于<Route component><Route render>,所以不要在同一个<Route>中使用多个。

path < string | string[]>
任何可以被 path-to-regexp@^1.7.0 识别的有效URL路径。

<Route path="/users/:id">
  <User />
</Route>
<Route path={["/users/:id", "/profile/:id"]}>
  <User />
</Route>

没有path 始终匹配的路线。

exact <boolean>
仅当路径 location.pathname 完全匹配时才匹配

<Route exact path="/one">
  <About />
</Route>
pathlocation.pathnameexactmatches?
/one/one/twotrueno
/one/one/twofalseyes

strict <boolean>
如果为true,带有尾斜杠的 path 将只与 location.pathname 带尾斜杠的路径名匹配,当在 location.pathname 中有额外的URL段时,这将不起作用

pathlocation.pathnamematches?
/one//oneno
/one//one/yes
/one//one/twoyes

Warning:可以使用strict来强制指定位置。pathname没有尾随斜杠,但为了做到这一点,strict和exact必须都为真

<Route exact strict path="/one">
  <About />
</Route>
pathlocation.pathnamematches?
/one//oneyes
/one//one/no
/one//one/twono

location <object>
<Route>元素尝试匹配 path 到当前的历史位置(通常是当前浏览器URL)。也可以传递 location,使不同的pathname进行匹配。
需要将<Route>匹配到某个位置而不是当前历史位置时,这是非常有用的,如Animated Transitions示例所示
如果<Route>元素被包裹在<Switch>中,并且与传递给<Switch>(或当前历史位置)的位置匹配,那么传递给<Route>的 location 将被<Switch>使用的 location 覆盖(这里给出)

sensitive <boolean>
当为true时,将匹配路径区分大小写。

<Route sensitive path="/one">
  <About />
</Route>
pathlocation.pathnamesensitivematches?
/one/onetrueyes
/One/onetrueno
/One/onefalseyes

<Router>

所有路由器组件的通用低级接口。通常,应用程序会使用一种高级路由器来代替:

import React from "react";
import ReactDOM from "react-dom";
import { Router } from "react-router";
import { createBrowserHistory } from "history";

const history = createBrowserHistory();

ReactDOM.render(
  <Router history={history}>
    <App />
  </Router>,
  node
);

history <object>
用于导航的 history 对象

import React from "react";
import ReactDOM from "react-dom";
import { createBrowserHistory } from "history";

const customHistory = createBrowserHistory();

ReactDOM.render(<Router history={customHistory} />, node);

children <element>
要渲染的子组件

<Router>
  <App />
</Router>

<StaticRouter>

永不改变位置的<Router>这在服务器端渲染场景中很有用,因为用户实际上并没有四处单击,所以位置实际上从未改变。因此,它的名字是:static。在简单的测试中,当您只需要插入一个位置并对渲染输出作出断言时,它也很有用。

这是一个示例节点服务器,它为发送302状态代码,<Redirect>并为其他请求发送常规HTML:

import http from "http";
import React from "react";
import ReactDOMServer from "react-dom/server";
import { StaticRouter } from "react-router";

http
  .createServer((req, res) => {
    // This context object contains the results of the render
    const context = {};

    const html = ReactDOMServer.renderToString(
      <StaticRouter location={req.url} context={context}>
        <App />
      </StaticRouter>
    );

    // context.url will contain the URL to redirect to if a <Redirect> was used
    if (context.url) {
      res.writeHead(302, {
        Location: context.url
      });
      res.end();
    } else {
      res.write(html);
      res.end();
    }
  })
  .listen(3000);

basename <string>
基准URL。basename 以 / 开头,但没有结尾的 /

<StaticRouter basename="/calendar">
  <Link to="/today"/> // renders <a href="/calendar/today">
</StaticRouter>

location <string>
服务器接收的URL,可能req.url在节点服务器上

<StaticRouter location={req.url}>
  <App />
</StaticRouter>

location <object>
类似 location 对象的 location 对象 同样具有{pathname, search, hash, state}

<StaticRouter location={{ pathname: "/bubblegum" }}>
  <App />
</StaticRouter>

context <object>
JavaScript对象。在渲染过程中,组件可以向对象添加属性来存储有关渲染的信息。

const context = {}
<StaticRouter context={context}>
  <App />
</StaticRouter>

<Route>匹配时,它会把 context 对象传递给它渲染的 staticContext 组件。请查看服务器渲染指南,了解更多关于如何自己做这件事的信息。
渲染之后,可以使用这些属性来配置服务器的响应。

if (context.status === "404") {
  // ...
}

children <element>
要渲染的子元素。
Note: 如果React < 16,必须使用单个子元素,因为render方法不能返回多个元素。如果您需要多个元素,则可以尝试将它们包装在extra中<div>

<Switch>

渲染成功匹配 path 的第一个子元素<Route><Redirect>

这与使用一堆<Route>s有什么不同呢?

<Switch>是唯一的,因为它只渲染一个<Route>。相反,每一个匹配位置的<Route>都会被包含。考虑这些路线:

import { Route } from "react-router";

let routes = (
  <div>
    <Route path="/about">
      <About />
    </Route>
    <Route path="/:user">
      <User />
    </Route>
    <Route>
      <NoMatch />
    </Route>
  </div>
);

如果URL是/about,则<About><User><NoMatch>将全部渲染,因为它们都与路径匹配。这是设计好的,允许我们以多种方式将<Route>组合到我们的应用程序中,比如侧边栏和面包屑,引导选项卡等。

然而,有时我们想只选择一个<Route>来渲染。如果 path 为/about,并不想匹配/:user(或者显示我们的“404”页面)。下面是如何用Switch做到这一点:

import { Route, Switch } from "react-router";

let routes = (
  <Switch>
    <Route exact path="/">
      <Home />
    </Route>
    <Route path="/about">
      <About />
    </Route>
    <Route path="/:user">
      <User />
    </Route>
    <Route>
      <NoMatch />
    </Route>
  </Switch>
);

现在,如果 path 为/about<Switch>将开始寻找匹配项<Route>。将只匹配<Route path="/about" />,并<Switch>停止寻找匹配项和渲染<About>等。同样,如果我们在/michael时,将只进行渲染<User>

location <object>
location对象,用于匹配子元素,而不是当前历史位置(通常是当前浏览器的URL)。

children <element>
<Switch>的所有子元素都应该是<Route><Redirect>元素。只会渲染与当前path匹配的第一个子元素。

<Route>元素使用它们的 path 属性来匹配,<Redirect>元素使用它们的from 属性来匹配。没有path 属性的<Route>或没有from 属性的<Redirect>将始终匹配当前位置。

当在<Switch>中包含<Redirect>时,它可以使用<Route>的任何 location 对象来匹配:path、exact和strict。from 只是 path 属性的别名。

将 location 属性提供给 <Switch>,它会覆盖匹配子元素上的 location 属性

import { Redirect, Route, Switch } from "react-router";

let routes = (
  <Switch>
    <Route exact path="/">
      <Home />
    </Route>

    <Route path="/users">
      <Users />
    </Route>
    <Redirect from="/accounts" to="/users" />

    <Route>
      <NoMatch />
    </Route>
  </Switch>
);

generatePath

可以使用generatePath函数生成路由的url。在内部使用path-to-regexp库。

import { generatePath } from "react-router";

generatePath("/user/:id/:entity(posts|comments)", {
  id: 1,
  entity: "posts"
});
// Will return /user/1/posts

将路径编译为正则表达式的结果会被缓存,因此使用相同的模式生成多条路径不会产生开销。

pattern <string>
generatePath 接受2个参数。第一个是作为Route组件的path属性提供。

params <object>
第二个参数是一个对象,它带有要使用的模式的相应参数。

如果提供的参数和路径不匹配,将抛出一个错误:

generatePath("/user/:id/:entity(posts|comments)", { id: 1 });
// TypeError: Expected "entity" to be defined

history

在本文档中,术语“history”和“history object”指的是 the history package,它是React路由器仅有的两个主要依赖项之一(除了React本身),它提供了在不同环境下用JavaScript管理会话历史的几种不同实现。

以下术语也会用到:

  • “browser history” - 特定于DOM的实现,在支持HTML5 history API的Web浏览器中很有用
  • “hash history” - 针对遗留web浏览器的特定于dom的实现
  • “memory history” - 内存中的 history 实现,在测试和非dom环境(如React Native)中很有用

history 对象通常具有以下属性和方法:

  • length <number> history 堆栈中的历史记录条目数
  • action <string> 当前操作(push,replace,pop)
  • location <object> location 对象,具有以下属性:

    • pathname <string> URL路径
    • search <string> URL查询字符串
    • hash <string> URL Hash 片段
    • state <object> 特定于location的状态,例如push(path, state)在将该位置推入堆栈时所提供的状态。仅在浏览器和内存历史记录中可用。
  • push(path, [state]) <function> 将新条目推入历史记录堆栈
  • replace(path, [state]) <function> 替换历史记录堆栈上的当前条目
  • go(n) <function> 将历史堆栈中的指针移动n个条目
  • goBack() <function> 等同于 go(-1)
  • goForward() <function> 等同于 go(1)
  • block(prompt) <function> 防止导航(请参阅历史记录文档

## history is mutable

history对象是可变的。因此,建议从<Route>的渲染属性中访问 location,而不是从history.location中。这可以确保React的假设在生命周期钩子中是正确的。例如:

class Comp extends React.Component {
componentDidUpdate(prevProps) {
  // will be true
  const locationChanged =
    this.props.location !== prevProps.location;

  // INCORRECT, will *always* be false because history is mutable.
  const locationChanged =
    this.props.history.location !== prevProps.history.location;
}
}

<Route component={Comp} />;

根据您使用的实现,还可能出现其他属性。更多细节请参考历史文档

location

location 代表应用程序现在的路径,需要前往的路径,或者之前访问过的路径。看起来像这样:

{
  key: 'ac3df4', // not with HashHistory!
  pathname: '/somewhere',
  search: '?some=search-string',
  hash: '#howdy',
  state: {
    [userDefined]: true
  }
}

路由器会在以下几个地方为你提供location对象:

history 上也有记录。但不推荐使用,因为它是可变的。你可以在history doc中读到更多。

componentWillReceiveProps(nextProps) {
  if (nextProps.location !== this.props.location) {
    // navigated!
  }
}

可以为导航的各个地方提供位置,而不是字符串:

  • Web Link to
  • Native Link to
  • Redirect to
  • history.push
  • history.replace

通常只需要一个path 字符串,但如果需要添加一些额外的信息,在应用返回到那个特定位置时可用,你可以使用一个位置对象代替。如果你想要基于导航历史而不是路径(如模式)来划分UI分支,这是非常有用的

// usually all you need
<Link to="/somewhere"/>

// but you can use a location instead
const location = {
  pathname: '/somewhere',
  state: { fromDashboard: true }
}

<Link to={location}/>
<Redirect to={location}/>
history.push(location)
history.replace(location)

最后,可以传递一个location 给以下组件:

这将防止他们使用路由器状态中的实际位置。这对于动画和待定导航,或者任何时候你想让一个组件在一个不同于真实组件的位置渲染都是很有用的。

match

match对象包含了关于 <Route path> 如何匹配URL的信息。match对象包含以下属性

  • params <object> 从URL解析到的键/值对
  • isExact <boolean> 如果匹配整个URL(没有末尾字符)则为true
  • path <string> 用于匹配的path模式。用于构建嵌套的<Route>s
  • url <string> URL的匹配部分。对于构建嵌套<Link>s

可以在不同的地方访问 match 对象:

如果 Route 中没有 path,因此总是进行匹配,将获得最接近的父项匹配项。规则同样适用withRouter

null matches
使用children属性的<Route>将调用其 children 函数,即使Route的 path 与当前 location 不匹配。在这种情况下,match将会是null。能够在<Route>匹配时渲染的内容可能会很有用,但是这种情况会带来一些挑战。

“resolve” url的默认方法是加入 match.url 字符串到“relative” 路径。

let path = `${match.url}/relative-path`;

如果在 match 为null时尝试执行此操作,则最终会出现TypeError。这意味着,当使用children属性时,尝试在<Route>内部连接“relative”路径是不安全的。

在生成空匹配对象的<Route>内部使用无路径<Route>时,会出现类似但更微妙的情况

// location.pathname = '/matches'
<Route path="/does-not-match"
  children={({ match }) => (
    // match === null
    <Route
      render={({ match: pathlessMatch }) => (
        // pathlessMatch === ???
      )}
    />
  )}
/>

没有path属性<Route>s从其父对象继承匹配对象。如果其父匹配项为空,则其匹配项也将为空。这意味着
a)任何子路由/链接都必须是绝对的,因为没有要解析的父级;
b)父级匹配可以为null的无路径路由需要使用使用children 属性进行渲染

matchPath

这使您可以使用与<Route>相同的匹配代码,除了正常渲染周期之外,例如在服务器上渲染之前收集数据依赖项。

import { matchPath } from "react-router";

const match = matchPath("/users/123", {
  path: "/users/:id",
  exact: true,
  strict: false
});

pathname
第一个参数是您想要匹配的路径名.如果您在服务器上使用Node。它会是req.path。

props
第二个参数是要匹配的props,它们与 Route 所接受的 match 属性相同。它们与Route所接受的match 属性相同。它也可以是一个字符串或字符串数组,作为{path}的快捷方式:

returns
当提供的 pathname 与 path 属性匹配时,它会返回一个对象

matchPath("/users/2", {
  path: "/users/:id",
  exact: true,
  strict: true
});

//  {
//    isExact: true
//    params: {
//        id: "2"
//    }
//    path: "/users/:id"
//    url: "/users/2"
//  }

当提供的 pathname 与 path 属性不匹配时,它返回null。

matchPath("/users", {
  path: "/users/:id",
  exact: true,
  strict: true
});

//  null

withRouter

非路由组件可以通过withRouter高阶组件访问 History 对象的属性和进行<Route>匹配。withRouter将在渲染时向包装组件传递更新的 match、location 和 history 属性。

{
  path, // like /users/:id; either a single string or an array of strings
  strict, // optional, defaults to false
  exact, // optional, defaults to false
}
import React from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router";

// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component {
  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  };

  render() {
    const { match, location, history } = this.props;

    return <div>You are now at {location.pathname}</div>;
  }
}

// Create a new component that is "connected" (to borrow redux
// terminology) to the router.
const ShowTheLocationWithRouter = withRouter(ShowTheLocation);

重要提示

withRouter 不订阅 location 更改,就像React Redux的connect订阅状态更改一样。而是在location 更改后从<Router>组件传播出来,然后重新渲染。这意味着,withRouter它不会对route的改变重新渲染,除非它的父组件重新渲染。

静态方法和属性

withRouter 包装组件的所有非特定于react的静态方法和属性都会自动复制到“connected”组件。

Component.WrappedComponent

被包装的组件作为静态属性公开,除其他事项外,该属性可用于隔离测试该组件。

// MyComponent.js
export default withRouter(MyComponent)

// MyComponent.test.js
import MyComponent from './MyComponent'
render(<MyComponent.WrappedComponent location={{...}} ... />)

wrapperComponentRef<function>

函数将作为ref道具传递给被包装的组件。

class Container extends React.Component {
  componentDidMount() {
    this.component.doSomething();
  }

  render() {
    return (
      <MyComponent wrappedComponentRef={c => (this.component = c)} />
    );
  }
}

结语

更新于 2021.2.7


C先生
98 声望8 粉丝