【问题标题】:How to get params in parent route component如何在父路由组件中获取参数
【发布时间】:2024-10-08 11:35:02
【问题描述】:

我正在使用 React、React-Router (v5) 和 Redux 构建一个应用,并且想知道如何访问父路由中当前 URL 的参数。

这是我的条目,router.js

<Wrapper>
  <Route exact path="/login" render={(props) => (
    <LoginPage {...props} entryPath={this.entryPath} />
  )} />

  <Route exact path="/" component={UserIsAuthenticated(HomePage)} />
  <Route exact path="/about" component={UserIsAuthenticated(AboutPage)} />
  <Route path="/projects" component={UserIsAuthenticated(ProjectsPage)} />
</Wrapper>

这就是我的ProjectsPage 组件:

class ProjectsPage extends PureComponent {
  componentDidMount() {
    this.props.fetchProjectsRequest()
  }

  render() {
    console.log(this.props.active)

    if (this.props.loading) {
      return <Loading />
    } else {
      return (
        <Wrapper>
          <Sidebar>
            <ProjectList projects={this.props.projects} />
          </Sidebar>
          <Content>
            <Route exact path="/projects" component={ProjectsDashboard} />

            <Switch>
              <Route exact path="/projects/new" component={ProjectNew} />
              <Route exact path="/projects/:id/edit" component={ProjectEdit} />
              <Route exact path="/projects/:id" component={ProjectPage} />
            </Switch>
          </Content>
        </Wrapper>
      )
    }
  }
}

const enhance = connect(
  (state, props) => ({
    active: props.match,
    loading: projectSelectors.loading(state),
    projects: projectSelectors.projects(state)
  }),
  {
    fetchProjectsRequest
  }
)

export default withRouter(enhance(ProjectsPage))

问题是,我的render 方法中的console.log 输出是{"path":"/projects","url":"/projects","isExact":false,"params":{}},尽管URL 是http://localhost:3000/projects/14

我想在我的ProjectList 中添加一个ID 属性以突出显示当前选定的项目。

我可以将项目的 ID 保存在我的 ProjectPage 组件内的商店中,但我认为这会有点混乱,特别是因为 URL 实际上包含信息 - 那么我为什么要在商店中写一些东西呢?

另一种(不好的?)方法是自己解析位置对象以获取 ID,但我认为此时有一种 react-router/react-router-redux 方法可以获取我忽略的参数。

【问题讨论】:

    标签: reactjs react-router react-redux


    【解决方案1】:

    @Kyle 从技术角度很好地解释了问题。 我将只专注于解决该问题。

    您可以使用matchPath 获取所选项目的id

    ma​​tchPath - https://reacttraining.com/react-router/web/api/matchPath

    这使您可以使用相同的匹配代码,除了 在正常的渲染周期之外,比如收集数据 在服务器上渲染之前的依赖关系。

    在这种情况下的使用非常简单。

    1 使用matchPath

    // history is one of the props passed by react-router to component
    // @link https://reacttraining.com/react-router/web/api/history
    
    const match = matchPath(history.location.pathname, {
      // You can share this string as a constant if you want
      path: "/articles/:id"
    });
    
    let articleId;
    
    // match can be null
    if (match && match.params.id) {
      articleId = match.params.id;
    }
    

    2 在渲染中使用articleId

    {articleId && (
      <h1>You selected article with id: {articleId}</h1>
    )}
    

    我构建了一个简单的演示,您可以使用它在您的项目中实现相同的功能。

    演示: https://codesandbox.io/s/pQo6YMZop

    我认为这个解决方案非常优雅,因为我们使用官方的react-router API,它也用于路由器中的路径匹配。我们也没有在这里使用window.location,因此如果您还导出原始组件,测试/模拟将很容易。

    【讨论】:

    • 这太棒了!这就是我一直在寻找的:-) 目前我正在使用router-parser,但使用react-router 包中的方法绝对有意义。事实上,我已经在路由中使用常量了 :-)
    【解决方案2】:

    TLDR

    如果您的 &lt;Route /&gt; path 属性不包含 :params,则 React 路由器 match.params 将是一个空对象。

    解决这个用例:您可以在父路由的组件中使用 let id = window.location.pathname.split("/").pop(); 来获取 id。

    详细原因

    如果提供给&lt;Route /&gt;path 属性没有任何参数,例如/:id,则react router 不会为您进行解析。如果您查看第 56 行的 matchPath.js,您可以开始了解 match 属性是如何构造的。

    return {
      path: path, // the path pattern used to match
      url: path === '/' && url === '' ? '/' : url, // the matched portion of the URL
      isExact: isExact, // whether or not we matched exactly
      params: keys.reduce(function (memo, key, index) {
          memo[key.name] = values[index];
          return memo;
      }, {})
    };
    

    然后我们可以查看第 43 行,您可以看到 keys 来自 _compilePath.keys。然后我们可以查看compilePath 函数,并看到它使用pathToRegexp(),它将使用stringToRegexp(),它将使用tokensToRegExp(),然后将keys 在第355 行与keys.push(token) 发生变异。对于在其path 属性中没有参数值的&lt;Route /&gt;stringToRegexp() 中使用的parse() 函数将不会返回任何标记,甚至不会到达第 #355 行,因为唯一的标记数组将不包含任何令牌对象。

    所以....如果您的&lt;Route /&gt; 没有:params,那么键值将是一个空数组,您在match 上将没有任何参数。

    总之,如果您的路线未将参数考虑在内,您似乎必须自己获取参数。你可以使用let id = window.location.pathname.split("/").pop()

    【讨论】: