javascript – React Router v4根级别无法访问的嵌套匹配参数

测试用例

https://codesandbox.io/s/rr00y9w2wm

重现步骤

>点击Topics
>点击Rendering with React

要么

>转到https://rr00y9w2wm.codesandbox.io/topics/rendering

预期的行为

> match.params.topicId应该是相同的,在Topic组件中访问时,父组件组件应与match.params.topicId相同

实际行为

>在主题组件中访问时,match.params.topicId未定义
> match.params.topicId在Subject组件中访问时正在呈现

我从this closed issue了解到这不一定是个bug.

此要求在想要在工厂Web应用程序中创建运行的用户中非常常见,其中组件父级别的主题需要访问match.params.paramId其中paramId是与嵌套(子)组件匹配的URL参数主题:

const Topic = ({ match }) => (
  <div>
    <h2>Topic ID param from Topic Components</h2>
    <h3>{match.params.topicId}</h3>
  </div>
);

const Topics = ({ match }) => (
  <div>
    <h2>Topics</h2>
    <h3>{match.params.topicId || "undefined"}</h3>
    <Route path={`${match.url}/:topicId`} component={Topic} />
    ...
  </div>
);

在一般意义上,主题可以是抽屉或导航菜单组件,主题可以是任何子组件,就像我正在开发的应用程序中一样.子组件有它自己的:topicId param,它有自己的(比方说)< Route path =“sections /:sectionId”component = {Section} />路线/组件.

更痛苦的是,导航菜单不需要与组件树具有一对一的关系.有时菜单根级别的项目(比如主题,章节等)可能对应于嵌套结构(章节仅在主题下呈现,/ topics /:topicId / sections /:sectionId虽然它有自己的规范化列表用户可以在导航栏的标题下使用该标题.
因此,单击“节”时,应突出显示该节,而不是“节”和“主题”.

由于sectionId或sections路径对应用程序的根级别的导航栏组件不可用,因此有必要为这种常见用例编写hacks like this.

我在React Router上根本不是专家,所以如果有人能为这个用例冒险一个合适的优雅解决方案,我会认为这是一项富有成效的努力.优雅,我的意思是

>使用匹配而不是history.location.pathname
>不涉及手动解析window.location.xxx等hacky方法
>不使用this.props.location.pathname
>不使用第三方库,如path-to-regexp
>不使用查询参数

其他黑客/部分解决方案/相关问题:

> React Router v4 – How to get current route?
> React Router v4 global no match to nested route childs

TIA!

解决方法:

尝试使用查询参数?允许父级和子级访问当前选定的主题.不幸的是,您需要使用模块qs,因为react-router-dom不会自动解析查询(react-router v3会这样做).

工作实例:https://codesandbox.io/s/my1ljx40r9

URL的结构类似于连接字符串:

主题?主题=道具-V状态

然后你会用&添加到查询中:

?/主题/主题=优化和放大器;类别=纯组件和放大器;子类= shouldComponentUpdate

✔使用匹配路由URL处理

✔不使用this.props.location.pathname(使用this.props.location.search)

✔使用qs解析location.search

✔不涉及hacky方法

Topics.js

import React from "react";
import { Link, Route } from "react-router-dom";
import qs from "qs";
import Topic from "./Topic";

export default ({ match, location }) => {
  const { topic } = qs.parse(location.search, {
    ignoreQueryPrefix: true
  });

  return (
    <div>
      <h2>Topics</h2>
      <ul>
        <li>
          <Link to={`${match.url}/topic?topic=rendering`}>
            Rendering with React
          </Link>
        </li>
        <li>
          <Link to={`${match.url}/topic?topic=components`}>Components</Link>
        </li>
        <li>
          <Link to={`${match.url}/topic?topic=props-v-state`}>
            Props v. State
          </Link>
        </li>
      </ul>
      <h2>
        Topic ID param from Topic<strong>s</strong> Components
      </h2>
      <h3>{topic && topic}</h3>
      <Route
        path={`${match.url}/:topicId`}
        render={props => <Topic {...props} topic={topic} />}
      />
      <Route
        exact
        path={match.url}
        render={() => <h3>Please select a topic.</h3>}
      />
    </div>
  );
};

另一种方法是创建一个将params存储到状态的HOC,并且当params更改时,子节点更新父节点的状态.

URL的结构类似于文件夹树:/ topics / rendering / optimization / pure-components / shouldComponentUpdate

工作示例:https://codesandbox.io/s/9joknpm9jy

✔使用匹配路由URL处理

✔不使用this.props.location.pathname

✔使用lodash对象进行对象比较

✔不涉及hacky方法

Topics.js

import map from "lodash/map";
import React, { Fragment, Component } from "react";
import NestedRoutes from "./NestedRoutes";
import Links from "./Links";
import createPath from "./createPath";

export default class Topics extends Component {
  state = {
    params: "",
    paths: []
  };

  componentDidMount = () => {
    const urlPaths = [
      this.props.match.url,
      ":topicId",
      ":subcategory",
      ":item",
      ":lifecycles"
    ];
    this.setState({ paths: createPath(urlPaths) });
  };

  handleUrlChange = params => this.setState({ params });

  showParams = params =>
    !params
      ? null
      : map(params, name => <Fragment key={name}>{name} </Fragment>);

  render = () => (
    <div>
      <h2>Topics</h2>
      <Links match={this.props.match} />
      <h2>
        Topic ID param from Topic<strong>s</strong> Components
      </h2>
      <h3>{this.state.params && this.showParams(this.state.params)}</h3>
      <NestedRoutes
        handleUrlChange={this.handleUrlChange}
        match={this.props.match}
        paths={this.state.paths}
        showParams={this.showParams}
      />
    </div>
  );
}

NestedRoutes.js

import map from "lodash/map";
import React, { Fragment } from "react";
import { Route } from "react-router-dom";
import Topic from "./Topic";

export default ({ handleUrlChange, match, paths, showParams }) => (
  <Fragment>
    {map(paths, path => (
      <Route
        exact
        key={path}
        path={path}
        render={props => (
          <Topic
            {...props}
            handleUrlChange={handleUrlChange}
            showParams={showParams}
          />
        )}
      />
    ))}
    <Route
      exact
      path={match.url}
      render={() => <h3>Please select a topic.</h3>}
    />
  </Fragment>
);
上一篇:使用PHP / Symfony在URL中获得#part


下一篇:javascript – Angular 2在子节点中添加指向根路由器的routerLink