小编典典

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

reactjs

测试用例

https://codesandbox.io/s/rr00y9w2wm

重现步骤

要么

预期行为

  • match.params.topicId应该是相同的来自两个亲本 的主题 组件应该是相同的match.params.topicId,当内访问 主题 部件

实际行为

  • match.params.topicIdTopic 组件内访问时 未定义
  • match.params.topicIdTopics 组件中访问时 呈现

这个已解决的问题中我了解到,不一定是错误。

在需要在工厂Web应用程序中创建运行的用户中,这种要求是非常普遍的,其中Topics父级组件需要访问 match.params.paramId
,其中paramIdURL参数与嵌套(子)组件相匹配Topic

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

从一般意义上讲,Topics它可以是“抽屉”或“导航菜单”组件,Topic也可以是任何子组件,就像我正在开发的应用程序中一样。子组件具有其自己的:topicId参数,该参数具有其自己(例如)<Route path="sections/:sectionId" component={Section} /> 路由/组件。

更加痛苦的是,导航菜单不必与组件树具有一对一的关系。有时,菜单根级的项目(例如TopicsSections等)可能与 嵌套
结构相对应(Sections仅在“主题”下呈现,/topics/:topicId/sections/:sectionId尽管它具有自己的标准化列表,用户可以在“导航
中的“标题 下找到该列表)酒吧)。因此,当 被点击, 应当强调,而不是两个 章节 主题

由于导航栏组件的sectionIdsections路径在应用程序的根目录级别不可用,因此对于这种常见的用例,有必要像这样编写hack

我根本不是React Router的专家,因此,如果任何人都可以对此用例提出合适的优雅解决方案,我将认为这是富有成果的努力。优雅,我的意思是

  • 用途match与非history.location.pathname
  • 不涉及诸如手动解析 window.location.xxx
  • 不使用 this.props.location.pathname
  • 不使用像 path-to-regexp
  • 不使用查询参数

TIA!


阅读 253

收藏
2020-07-22

共1个答案

小编典典

尝试利用查询参数?来允许父级和子级访问当前选定的topic。不幸的是,您将需要使用模块qs,因为react- router-dom它不会自动解析查询(react-router v3可以)。

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

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

topic?topic=props-v-state

然后您将添加到查询中&

/topics/topic?topic=optimization&category=pure- components&subcategory=shouldComponentUpdate

✔使用匹配进行路由URL处理

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

✔用于qs解析location.search

✔不涉及骇客方法

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

另一种方法是创建一个HOC将参数存储到的state子项,state当子项的参数更改时,子项会更新其父项。

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

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

✔使用匹配进行路由URL处理

✔不使用 this.props.location.pathname

✔使用lodash进行对象间比较

✔不涉及骇客方法

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>
    );
2020-07-22