本文介绍了React Router v4 嵌套匹配参数在根级别无法访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
测试用例
https://codesandbox.io/s/rr00y9w2wm
重现步骤
或
预期行为
match.params.topicId
应该与父 Topics 组件相同,访问时应该与 match.params.topicId
相同在主题组件 中
实际行为
match.params.topicId
在 Topic 组件中访问时未定义match.params.topicId
在 Topics 组件中访问时是呈现
我从这个已解决的问题了解到这不一定是一个错误.
此要求在想要在工厂 Web 应用程序中创建运行的用户中非常普遍,其中父级的组件 Topics
需要访问 match.params.paramId 其中 paramId
是与嵌套(子)组件 Topic
匹配的 URL 参数:
const Topic = ({ match }) =>(<div><h2>来自主题组件的主题 ID 参数</h2><h3>{match.params.topicId}</h3>
);const 主题 = ({ 匹配 }) =>(<div><h2>主题</h2><h3>{match.params.topicId ||未定义"}<路由路径={`${match.url}/:topicId`} component={Topic}/>...
);
在一般意义上,Topics
可以是抽屉或导航菜单组件,Topic
可以是任何子组件,就像我正在开发的应用程序一样.子组件有它自己的 :topicId
参数,它有它自己的(假设)<Route path="sections/:sectionId" component={Section}/>
路由/组件.
更痛苦的是,导航菜单不需要与组件树具有一对一的关系.有时,菜单根级别的项目(比如Topics
、Sections
等)可能对应于一个嵌套结构(Sections
仅在一个主题下呈现,/topics/:topicId/sections/:sectionId
尽管它有自己的规范化列表,在标题 Sections下可供用户使用强> 在导航栏中).因此,当点击部分时,它应该突出显示,而不是同时突出显示部分和主题.
如果 sectionId
或 sections
路径对位于应用程序根级别的导航栏组件不可用,则需要编写 像这样的 hacks 用于这种常见的用例.
我在 React Router 方面根本不是专家,所以如果有人可以为这个用例冒险一个适当的优雅解决方案,我会认为这是一项富有成效的努力.优雅,我的意思是
- 使用
match
而不是 history.location.pathname
- 不涉及手动解析
window.location.xxx
等 hacky 方法 - 不使用
this.props.location.pathname
- 不使用像
path-to-regexp
这样的第三方库- 不使用查询参数
其他黑客/部分解决方案/相关问题:
React Router v4 - 如何获取当前路由?
React Router v4 全局不匹配嵌套路由子项
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
✔ 不涉及hacky方法
Topics.js
从react"导入React;从react-router-dom"导入{链接,路由};从qs"导入 qs;从./Topic"导入主题;导出默认值({匹配,位置})=>{const { 主题 } = qs.parse(location.search, {忽略查询前缀:true});返回 (<div><h2>主题</h2><ul><li><链接到={`${match.url}/topic?topic=rendering`}>使用 React 渲染</链接><li><Link to={`${match.url}/topic?topic=components`}>组件</Link><li><链接到={`${match.url}/topic?topic=props-v-state`}>道具诉国家</链接><h2>来自 Topics 的主题 ID 参数组件<h3>{主题&&主题}<路线path={`${match.url}/:topicId`}渲染={道具=><主题{...道具}主题={主题}/>}/><路线精确的路径={匹配.url}渲染={() =><h3>请选择一个主题.</h3>}/>
);};
另一种方法是创建一个 HOC
将参数存储到 state
并且当它的参数改变时孩子更新父的 state
.
URL 的结构类似于文件夹树:/topics/rendering/optimization/pure-components/shouldComponentUpdate
工作示例:https://codesandbox.io/s/9joknpm9jy
✔ 使用匹配来处理路由 URL
✔ 不使用 this.props.location.pathname
✔ 使用 lodash 进行对象间比较
✔ 不涉及hacky方法
Topics.js
从lodash/map"导入地图;从反应"导入反应,{片段,组件};从./NestedRoutes"导入 NestedRoutes;从./Links"导入链接;从./createPath"导入createPath;导出默认类主题扩展组件{状态 = {参数:"",路径:[]};componentDidMount = () =>{const urlPaths = [this.props.match.url,":topicId",":子类别",:物品",:生命周期"];this.setState({ 路径: createPath(urlPaths) });};handleUrlChange = params =>this.setState({ params });showParams = params =>!params?空值: map(params, name => );渲染 = () =>(<div><h2>主题</h2><链接匹配={this.props.match}/><h2>来自 Topics 的主题 ID 参数组件<h3>{this.state.params &&this.showParams(this.state.params)}<嵌套路由handleUrlChange={this.handleUrlChange}匹配={this.props.match}路径={this.state.paths}showParams={this.showParams}/>
);}
NestedRoutes.js
从lodash/map"导入地图;import React, { Fragment } from "react";从react-router-dom"导入{路由};从./Topic"导入主题;导出默认值({handleUrlChange,match,paths,showParams})=>(<片段>{地图(路径,路径=>(<路线精确的键={路径}路径={路径}渲染={道具=>(<话题{...道具}handleUrlChange={handleUrlChange}showParams={showParams}/>)}/>))}<路线精确的路径={匹配.url}渲染={() =><h3>请选择一个主题.</h3>}/></片段>);
Test Case
https://codesandbox.io/s/rr00y9w2wm
Steps to reproduce
OR
Expected Behavior
match.params.topicId
should be identical from both the parent Topics component should be the same as match.params.topicId
when accessed within the Topic component
Actual Behavior
match.params.topicId
when accessed within the Topic component is undefinedmatch.params.topicId
when accessed within the Topics component is rendering
I understand from this closed issue that this is not necessarily a bug.
This requirement is super common among users who want to create a run in the mill web application where a component Topics
at a parent level needs to access the match.params.paramId where paramId
is a URL param that matches a nested (child) component 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>
);
In a generic sense, Topics
could be a Drawer or Navigation Menu component and Topic
could be any child component, like it is in the application I'm developing. The child component has it's own :topicId
param which has it's own (let's say) <Route path="sections/:sectionId" component={Section} />
Route/Component.
Even more painful, the Navigation Menu needn't have a one-to-one relationship with the component tree. Sometimes the items at the root level of the menu (say Topics
, Sections
etc.) might correspond to a nested structure (Sections
is only rendered under a Topic, /topics/:topicId/sections/:sectionId
though it has its own normalized list that is available to the user under the title Sections in the Navigation Bar).Therefore, when Sections is clicked, it should be highlighted, and not both Sections and Topics.
With the sectionId
or sections
path unavailable to the Navigation Bar component which is at the Root level of the application, it becomes necessary to write hacks like this for such a commonplace use case.
I am not an expert at all at React Router, so if anyone can venture a proper elegant solution to this use case, I would consider this to be a fruitful endeavor. And by elegant, I mean
- Uses
match
and not history.location.pathname
- Does not involve hacky approaches like manually parsing the
window.location.xxx
- Doesn't use
this.props.location.pathname
- Does not use third party libraries like
path-to-regexp
- Does not use query params
Other hacks/partial solutions/related questions:
React Router v4 - How to get current route?
React Router v4 global no match to nested route childs
TIA!
解决方案
Try utilizing query parameters ?
to allow the parent and child to access the current selected topic
. Unfortunately, you will need to use the module qs because react-router-dom
doesn't automatically parse queries (react-router v3 does).
Working example: https://codesandbox.io/s/my1ljx40r9
URL is structured like a concatenated string:
topic?topic=props-v-state
Then you would add to the query with &
:
/topics/topic?topic=optimization&category=pure-components&subcategory=shouldComponentUpdate
✔ Uses match for Route URL handling
✔ Doesn't use this.props.location.pathname
(uses this.props.location.search
)
✔ Uses qs
to parse location.search
✔ Does not involve hacky approaches
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>
);
};
Another approach would be to create a HOC
that stores params to state
and children update the parent's state
when its params have changed.
URL is structured like a folder tree: /topics/rendering/optimization/pure-components/shouldComponentUpdate
Working example: https://codesandbox.io/s/9joknpm9jy
✔ Uses match for Route URL handling
✔ Doesn't use this.props.location.pathname
✔ Uses lodash for object to object comparison
✔ Does not involve hacky approaches
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>
);
这篇关于React Router v4 嵌套匹配参数在根级别无法访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!