当前位置: 首页 > 知识库问答 >
问题:

React路由器v4嵌套匹配参数在根级别不可访问

漆雕唯
2023-03-14

https://codesandbox.io/s/rr00y9w2wm

  • 单击主题
  • 单击“使用React渲染”

或者

  • 去https://rr00y9w2wm.codesandbox.io/topics/rendering
  • match.params.topicId应与父主题组件相同,在主题组件中访问时应与match.params.topicId相同
  • match.params.topicId在主题组件中访问时未定义

我从这个已结束的问题中了解到,这不一定是一个bug。

这个需求在想要在工厂Web应用程序中创建运行的用户中非常常见,其中父级的组件Topics需要访问match.params.paramId,其中参数Id是匹配的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>
);

在一般意义上,主题可以是抽屉或导航菜单组件,主题可以是任何子组件,就像我正在开发的应用程序一样。子组件有它自己的: topicId参数,它有它自己的(比方说)

更痛苦的是,导航菜单不需要与组件树有一对一的关系。有时菜单根级别的项(例如TopicsSections等)可能对应于嵌套结构(Sections仅在Topic下呈现,但/主题/: topicId/节/: sectionId它有自己的标准化列表,用户可以在导航栏的标题部分下使用该列表)。因此,当点击部分时,应该突出显示,而不是同时点击部分和主题。

由于位于应用程序根级别的导航栏组件无法使用sectionIdsections路径,因此有必要为这种常见的用例编写类似的hack。

我不是一个专家在所有的反应路由器,所以如果任何人可以冒险一个适当的优雅解决这个用例,我会认为这是一个富有成效的努力。我指的是优雅

  • 使用匹配而不是历史记录.位置.路径名

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

>

  • React路由器v4-如何获取当前路由?

    React路由器v4全局不匹配嵌套路由子节点

    蒂亚!


  • 共有3个答案

    赵志
    2023-03-14

    如果你有一组已知的子路由,那么你可以使用这样的东西:

    Import {BrowserRouter as Router, Route, Switch } from 'react-router-dom'
    
     <Router>
      <Route path={`${baseUrl}/home/:expectedTag?/:expectedEvent?`} component={Parent} />
    </Router>
    const Parent = (props) => {
        return (
           <div >
             <Switch>
             <Route path={`${baseUrl}/home/summary`} component={ChildOne} />
             <Route
               path={`${baseUrl}/home/:activeTag/:activeEvent?/:activeIndex?`}
               component={ChildTwo}
              />
             </Switch> 
           <div>
          )
        }
    

    在上面的示例中,父组件将获取expectedTag、expectedEvent作为匹配参数,并且与子组件没有冲突,子组件将获取activeTag、activeEvent、activeIndex作为参数。参数也可以使用相同的名称,我也尝试过。

    平元明
    2023-03-14

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

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

    URL的结构类似于串联字符串:

    topic?topic=props-v-state

    然后使用

    <代码>/主题/主题?优化主题

    ✔ 使用匹配进行路由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>
      );
    };
    

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

    邹嘉致
    2023-03-14

    React router不提供任何匹配子路由的匹配参数,而是根据当前匹配提供参数。所以如果你有你的路线设置像

    <Route path='/topic' component={Topics} />
    

    主题组件中,您有一个类似

    <Route path=`${match.url}/:topicId` component={Topic} />
    

    现在,如果您的url是与内部路由匹配的/topic/topic1,但对于Topics组件,匹配的路由仍然是/topic,因此其中没有参数,这是有意义的。

    如果您想要获取主题组件中匹配的子路由的参数,您需要使用React router提供的matchPath实用程序,并针对您想要获取其参数的子路由进行测试

    import { matchPath } from 'react-router'
    
    render(){
        const {users, flags, location } = this.props;
        const match = matchPath(location.pathname, {
           path: '/topic/:topicId',
           exact: true,
           strict: false
        })
        if(match) {
            console.log(match.params.topicId);
        }
        return (
            <div>
                <Route exact path="/topic/:topicId" component={Topic} />
            </div>
        )
    }
    

    编辑:

    获取任何级别的所有参数的一种方法是利用上下文并在上下文提供程序中匹配时更新参数。

    您需要创建一个路由包装,使其正常工作,一个典型的例子如下

    RouteWrapper.jsx

    import React from "react";
    import _ from "lodash";
    import { matchPath } from "react-router-dom";
    import { ParamContext } from "./ParamsContext";
    import { withRouter, Route } from "react-router-dom";
    
    class CustomRoute extends React.Component {
      getMatchParams = props => {
        const { location, path, exact, strict } = props || this.props;
        const match = matchPath(location.pathname, {
          path,
          exact,
          strict
        });
        if (match) {
          console.log(match.params);
          return match.params;
        }
        return {};
      };
      componentDidMount() {
        const { updateParams } = this.props;
        updateParams(this.getMatchParams());
      }
      componentDidUpdate(prevProps) {
        const { updateParams, match } = this.props;
        const currentParams = this.getMatchParams();
        const prevParams = this.getMatchParams(prevProps);
        if (!_.isEqual(currentParams, prevParams)) {
          updateParams(match.params);
        }
      }
    
      componentWillUnmount() {
        const { updateParams } = this.props;
        const matchParams = this.getMatchParams();
        Object.keys(matchParams).forEach(k => (matchParams[k] = undefined));
        updateParams(matchParams);
      }
      render() {
        return <Route {...this.props} />;
      }
    }
    
    const RouteWithRouter = withRouter(CustomRoute);
    
    export default props => (
      <ParamContext.Consumer>
        {({ updateParams }) => {
          return <RouteWithRouter updateParams={updateParams} {...props} />;
        }}
      </ParamContext.Consumer>
    );
    

    ParamsProvider.jsx

    import React from "react";
    import { ParamContext } from "./ParamsContext";
    export default class ParamsProvider extends React.Component {
      state = {
        allParams: {}
      };
      updateParams = params => {
        console.log({ params: JSON.stringify(params) });
        this.setState(prevProps => ({
          allParams: {
            ...prevProps.allParams,
            ...params
          }
        }));
      };
      render() {
        return (
          <ParamContext.Provider
            value={{
              allParams: this.state.allParams,
              updateParams: this.updateParams
            }}
          >
            {this.props.children}
          </ParamContext.Provider>
        );
      }
    }
    

    Index.js

    ReactDOM.render(
      <BrowserRouter>
        <ParamsProvider>
          <App />
        </ParamsProvider>
      </BrowserRouter>,
      document.getElementById("root")
    );
    

    工作演示

     类似资料:
    • 问题内容: 测试用例 https://codesandbox.io/s/rr00y9w2wm 重现步骤 点击主题 点击React渲染 要么 转到https://rr00y9w2wm.codesandbox.io/topics/rendering 预期行为 应该是相同的来自两个亲本 的主题 组件应该是相同的,当内访问 主题 部件 实际行为 在 Topic 组件内访问时 未定义 在 Topics 组件

    • 我在想这样的事情: 前台有一个不同的布局和风格与管理区域。所以在frontpage中的路由是home、about等等,其中一个应该是子路由。 /home应该呈现到Frontpage组件中,而/admin/home应该呈现在后端组件中。 最终解决方案: 这是我现在使用的最终解决方案。这个例子也像传统的404页面一样有一个全局错误组件。

    • 问题内容: 我已经在应用程序中将React Router升级到了版本4。但是现在我得到了错误 此路由有什么问题? 问题答案: IndexRoute和browserHistory在最新版本中不可用,并且Routes不接受带有v4的子级Routes,您可以在组件本身中指定Routes 然后在主要部分 同样在汽车组件中 您将拥有

    • 关于未解决的问题(作为最终结论) react路由器dom v4中的多个嵌套路由 如何在React路由器v4中嵌套路由 我也有同样的问题。 https://reacttraining.com/react-router/web/guides/quick-start促进react-router-dom 此外,人们发现最好在一个文件中列出路由,而不是在组件内部。 有人提到:https://github.c

    • 问题内容: 我有以下路由配置: GuaranteeLoggedInContainer为: 但是,历史的推动力:没有用。这里没有历史。 如果我使用这样的配置: 我遇到类似的问题: reactjs中最好的身份验证方法是什么? 问题答案: 从我对您的React Router设计的了解中,您似乎正在使用React Router版本4 在这种情况下,您可以在组件本身中指定路由,并利用withRouter进行

    • 问题内容: 有没有办法在React Router v4中嵌套路由? 这有效: 这不是: 客户组成部分: 问题答案: 到目前为止,我发现的最佳模式。 我可以继续将其嵌套在组件中,并且一切都很好,包括hmr(如果使用webpack,请不要忘记设置为)