https://codesandbox.io/s/rr00y9w2wm
OR
match.params.topicId
は、両方の親から同一である必要がありますTopicsコンポーネントは、Topicコンポーネント内でアクセスする場合、match.params.topicId
と同じである必要がありますmatch.params.topicId
Topicコンポーネント内でアクセスした場合未定義match.params.topicId
Topicsコンポーネント内でアクセスされた場合レンダリングthis closed issue から、これは必ずしもバグではないことを理解しています。
この要件は、親レベルのコンポーネントTopics
がmatch.params.paramIdにアクセスする必要があるミルWebアプリケーションで実行を作成するユーザーの間で非常に一般的です。ここで、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
はDrawerまたはNavigation Menuコンポーネントであり、Topic
は、私が開発しているアプリケーションのように、任意の子コンポーネントです。子コンポーネントには独自の:topicId
paramがあり、これには独自の(たとえば)<Route path="sections/:sectionId" component={Section} />
Route/Componentがあります。
さらに苦しいのは、ナビゲーションメニューがコンポーネントツリーと1対1の関係を持つ必要がないことです。メニューのルートレベルの項目(Topics
、Sections
など)は、nested構造に対応する場合があります(Sections
は、トピック/topics/:topicId/sections/:sectionId
にのみ表示されますが、独自のナビゲーションバーのタイトルSectionsでユーザーが使用できる正規化されたリスト)。したがって、Sectionsをクリックすると、itが強調表示され、Sectionsとトピック。
sectionId
またはsections
パスがアプリケーションのルートレベルにあるナビゲーションバーコンポーネントに使用できない場合、このような一般的なユースケースでは このようなハッキング を記述する必要があります。
私はReact Routerの専門家ではありません。だから誰かがこのユースケースに適切でエレガントなソリューションをベンチャーできるなら、これは実り多い努力だと思います。
history.location.pathname
ではなく、match
を使用しますwindow.location.xxx
を手動で解析するようなハッキング手法は含まれませんthis.props.location.pathname
を使用しませんpath-to-regexp
などのサードパーティライブラリを使用しませんその他のハッキング/部分的な解決策/関連する質問:
TIA!
クエリパラメータ?
を利用して、親と子が現在選択されているtopic
にアクセスできるようにしてください。残念ながら、react-router-dom
はクエリを自動的に解析しないため、モジュール qs を使用する必要があります(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>
);
};
別のアプローチは、HOC
にパラメーターを保存するstate
を作成し、パラメーターが変更されたときに子が親の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>
);
React-router
は、一致した子Routeの一致パラメーターを提供しませんが、現在の一致に基づいてパラメーターを提供します。したがって、次のようなルート設定がある場合
<Route path='/topic' component={Topics} />
Topics
コンポーネントには、次のようなルートがあります
<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>
)
}
編集:
任意のレベルですべてのパラメーターを取得する1つの方法は、コンテキストを利用し、コンテキストプロバイダーで一致したときにパラメーターを更新することです。
Routeを正しく動作させるには、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>
);
}
}
Index.js
ReactDOM.render(
<BrowserRouter>
<ParamsProvider>
<App />
</ParamsProvider>
</BrowserRouter>,
document.getElementById("root")
);
次のようなことを試してください。
_<Switch>
<Route path="/auth/login/:token" render={props => <Login {...this.props} {...props}/>}/>
<Route path="/auth/login" component={Login}/>
_
最初に、パラメータ付きのルートと、パラメータなしのリンクの後。 Loginコンポーネントの中に、このコード行console.log(props.match.params.token);
を入れてテストし、私のために働きました。