草庐IT

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

coder 2024-05-16 原文

测试用例

https://codesandbox.io/s/rr00y9w2wm

重现步骤

预期行为

  • match.params.topicId应该与父 Topics 组件相同应该与 match.params.topicId 相同在 主题 组件中访问时

实际行为

  • match.params.topicIdTopic 组件中访问时是 undefined
  • match.params.topicIdTopics 组件中访问时呈现

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

此要求在想要在组件 Topics 的工厂 Web 应用程序中创建运行的用户中非常普遍。在父级别需要访问 ma​​tch.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 仅在主题下呈现,/topics/:topicId/sections/:sectionId 虽然它有自己的规范化列表,用户可以在导航栏中的标题 Sections 下使用)。 因此,当点击Sections时,应该突出显示,而不是SectionsTopics.

随着 sectionIdsections应用程序根级别的导航栏组件的路径不可用,因此有必要编写 hacks like this对于这样一个常见的用例。

我根本不是 React Router 方面的专家,所以如果有人能为这个用例尝试一个合适的优雅解决方案,我会认为这是一项富有成果的尝试。优雅,我的意思是

  • 使用 match而不是 history.location.pathname
  • 不涉及手动解析 window.location.xxx 等骇人听闻的方法
  • 不使用 this.props.location.pathname
  • 不使用像path-to-regexp 这样的第三方库
  • 不使用查询参数

其他技巧/部分解决方案/相关问题:

  1. React Router v4 - How to get current route?

  2. React Router v4 global no match to nested route childs

TIA!

最佳答案

React-router 不会为您提供任何匹配的子 Route 的匹配参数,而是根据当前匹配为您提供参数。因此,如果您将路线设置为

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

Topics 组件中你有一个像这样的 Route

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

现在,如果您的 url 是 /topic/topic1,它与内部路由匹配,但对于主题组件,匹配的路由仍然是 /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>
    )
}

编辑:

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

您需要为 Route 创建一个包装器才能使其正常工作,一个典型的示例如下所示

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

索引.js

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

Working DEMO

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

有关javascript - React Router v4 嵌套匹配参数无法在根级别访问的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  3. ruby - 将散列转换为嵌套散列 - 2

    这道题是thisquestion的逆题.给定一个散列,每个键都有一个数组,例如{[:a,:b,:c]=>1,[:a,:b,:d]=>2,[:a,:e]=>3,[:f]=>4,}将其转换为嵌套哈希的最佳方法是什么{:a=>{:b=>{:c=>1,:d=>2},:e=>3,},:f=>4,} 最佳答案 这是一个迭代的解决方案,递归的解决方案留给读者作为练习:defconvert(h={})ret={}h.eachdo|k,v|node=retk[0..-2].each{|x|node[x]||={};node=node[x]}node[

  4. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  5. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  6. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  7. ruby - 匹配未转义的平衡定界符对 - 2

    如何匹配未被反斜杠转义的平衡定界符对(其本身未被反斜杠转义)(无需考虑嵌套)?例如对于反引号,我试过了,但是转义的反引号没有像转义那样工作。regex=/(?!$1:"how\\"#expected"how\\`are"上面的正则表达式不考虑由反斜杠转义并位于反引号前面的反斜杠,但我愿意考虑。StackOverflow如何做到这一点?这样做的目的并不复杂。我有文档文本,其中包括内联代码的反引号,就像StackOverflow一样,我想在HTML文件中显示它,内联代码用一些spanMaterial装饰。不会有嵌套,但转义反引号或转义反斜杠可能出现在任何地方。

  8. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  9. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  10. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

随机推荐