测试用例

https://codesandbox.io/s/rr00y9w2wm

重现步骤

  • 单击Topics
  • 单击Rendering with React

  • 或者
  • 转到https://rr00y9w2wm.codesandbox.io/topics/rendering

  • 预期行为
  • match.params.topicId应该与父相同。在主题组件
  • 中访问时,主题组件应与match.params.topicId相同。

    实际行为

    中访问时的
  • match.params.topicId主题组件是未定义
  • 中访问时的
  • match.params.topicId主题组件是呈现

  • 我从this closed issue了解这不一定是错误。

    在需要在工厂Web应用程序中创建运行的用户中,这种要求是非常普遍的,其中 parent 级别的组件Topics需要访问 match.params.paramId ,其中paramId是与嵌套(子级)匹配的URL参数组件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仅在Topic,/topics/:topicId/sections/:sectionId下呈现,尽管它具有自己的规范化列表,用户可以在下面使用标题(导航栏中的部分)。
    因此,当单击 Sections 时,应突出显示它,而不是 Sections 主题

    由于导航栏组件的sectionIdsections路径在应用程序的根目录下不可用,因此有必要为这种常见用例编写hacks like this

    我根本不是React Router的专家,因此,如果任何人都可以对此用例寻求合适的优雅解决方案,我都会认为这是富有成果的努力。优雅,我的意思是
  • 使用match而不使用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!

    最佳答案

    尝试利用查询参数?来允许父级和子级访问当前选定的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>
      );
    };
    

    另一种方法是创建一个将参数存储到HOCstate,并且当其参数更改时,子代会更新父级的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>
    );
    

    关于javascript - 在根级别无法访问React Router v4嵌套匹配参数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52684017/

    10-11 17:18