React Router如何递归地创建页面?

时间:2019-02-03 21:11:49

标签: javascript reactjs react-native react-router

我在这里谈论这个例子:https://reacttraining.com/react-router/web/example/recursive-paths

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

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);
}

function RecursiveExample() {
  return (
    <Router>
      <Person match={{ params: { id: 0 }, url: "" }} />
    </Router>
  );
}

function Person({ match }) {
  let person = find(match.params.id);

  return (
    <div>
      <h3>
        {person.name}
        ’s Friends
      </h3>
      <ul>
        {person.friends.map(id => (
          <li key={id}>
            <Link to={`${match.url}/${id}`}>{find(id).name}</Link>
          </li>
        ))}
      </ul>
      <Route path={`${match.url}/:id`} component={Person} />
    </div>
  );
}

export default RecursiveExample;

在此示例中,我了解了递归如何一次完成。令我感到困惑的是,什么时候(在那个小型浏览器中)刷新页面/1/0/3/2/3/2或任何其他嵌套示例后直接放置此链接,React Router如何知道以正确的方式呈现组件订购。没有匹配+ /:id1/:id2/:id3等的明确路由路径。

我知道这是不可行的,因为它可以达到无穷远,但是React Router如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

TLDR; 这里的主要技巧是,只要渲染的{{的Person 1}}与网址匹配。


详细信息

根据设计,仅当Route的Route道具与页面的URL匹配时,才会呈现传递给Route的path道具的组件。

Route

由于我们仅在点击时渲染嵌套的component(具有自己的path),因此我们可以无限期地生成这些递归路由。

每次渲染{/* Something will get rendered when the URL /whatever matches the URL of the page */} <Route path="/whatever" component={Something}> 时,它都会在其中渲染Person作为下一个Route的占位符。

Person

如果下一个Route的{​​{1}}与URL相匹配,则呈现下一个Person。拥有的function Person({ match }) { // ... return ( <div> {/* ... */} {/* this route is a placeholder for the next Person */} <Route path={`${match.url}/:id`} component={Person} /> </div> ); } 的{​​{1}}是父级的URL加上另一个ID,即Person

这就是我们获得递归渲染的方式。 Route渲染path,渲染Route,渲染path ...重复直到最后渲染的match.url + '/:id'的{​​{1}}页面网址不匹配。

注意:match propPerson传递到作为Route属性传递到该Person中的组件。 Route就是这种访问方式。

需要注意的一件事:由于以下原因,应用程序总是以Route的朋友(即pathRoute的人的朋友)开头,

component

这意味着无论您是第一次打开页面还是在Route刷新页面,React Router始终将Person渲染为第一个Michelle,这将渲染嵌套的{{1 }}。

这意味着应用程序的初始状态始终如下所示:

id

哪个呈现为:

app always starts with friends of Michelle

如果应用程序是在0加载的,则渲染将停止在上述状态,直到我们开始与该应用程序进行交互为止。

但是,如果我们刷新页面function RecursiveExample() { return ( <Router> {/* This part of the code passes in a fake `match` prop to always start of the app from Michelle's point of view */} <Person match={{ params: { id: 0 }, url: "" }} /> </Router> ); } ,则呈现将继续。呈现以上所示的初始状态后,React Router将发现/0/1/2中的MichellePerson URL匹配(通过使<Route path='/:id' component={Person} />忽略其余部分),填充<RecursiveExample> <BrowserRouter> <Person> <Route path="/:id" /> </Person> </BrowserRouter> </RecursiveExample> 作为/,并渲染下一个/0/1/2(将path作为<Route path="/:id" />的那个)。

注意:上面的URL匹配之所以有效,是因为/0/1/2的默认匹配策略是忽略路径末尾的任何多余字符。这就是0id匹配,0Personid部分被忽略的原因。我们可以使用exact prop强制匹配完全正确,但这会破坏递归行为,即0仅匹配Route而不匹配/:id

这时我们的树看起来像:

/0/1/2

在此阶段渲染的人恰好又是id(由于0 /1/2)。

Second stage also shows friends of Michelle

/:id现在重复该过程。它呈现了自己的嵌套/0,现在其嵌套/0/1/2<RecursiveExample> <BrowserRouter> <Person> <Route path="/:id" {/* this matches /0 */} <Person> {/* we're here during the rendering stage */} </Person> </Route> <Person> </BrowserRouter> </RecursiveExample> (因为它将Michelle连接到父URL),又id再次为{{1 }}。由于0仍与Person匹配(ID现在为Route),因此我们呈现下一个path

这时树是:

/0/:id

树现在显示/:id等于component的人的朋友:

Now we show friends of Sean

您能看到前进的方向吗?

重复此过程,直到最后渲染的Person中渲染的/0/:id的{​​{1}}与URL不匹配为止。在/0/1/2 URL刷新的情况下,将是1Person <RecursiveExample> <BrowserRouter> <Person> <Route path="/:id" {/* this matches /0 */} <Person> <Route path="/0/:id"> {/* this matches /0/1 */} <Person> {/* we're here during the rendering stage */} </Person> </Route> </Person> </Route> <Person> </BrowserRouter> </RecursiveExample>

这是最终的React树的样子:

id

这是最终的应用程序状态:

final rendered state


现在,无论我们实际上是刷新1上的页面还是单击链接进行访问,其行为都是相同的。

重要的是要记住,它是控制呈现内容的URL。

通过刷新或手动键入特定URL来启动应用程序时,React Router会自动完成嵌套的path渲染阶段(上面显示的前几个),只要每个渲染路径的路径都可以匹配网址。

另一方面,每次我们单击指向特定URL的链接时,我们都会手动触发URL更改,从而触发下一个Route的呈现阶段。

我们是手动键入URL还是通过单击创建URL,如果结果URL相同,则呈现的应用程序也相同。 区别在于我们如何达到最终的渲染状态。