https://codesandbox.io/s/rr00y9w2wm
OR
match.params.topicId
doit être identique à partir du composant parent Topics doit être identique à match.params.topicId
lorsque vous y accédez via le composant Topicmatch.params.topicId
lorsque vous y accédez par le composant Topic est undefinedmatch.params.topicId
lorsque vous y accédez par le composant Topics est renderingJe comprends depuis ce numéro fermé que ce n’est pas nécessairement un bug.
Cette exigence est très courante chez les utilisateurs qui souhaitent créer une exécution dans l'application Web de la fabrique dans laquelle un composant Topics
au niveau parent doit accéder à match.params.paramId où paramId
est un paramètre d'URL qui correspond à un imbriqué ( enfant) composant 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>
);
De manière générique, Topics
pourrait être un composant Tiroir ou Menu de navigation et Topic
pourrait être n'importe quel composant enfant, comme dans l'application que je développe. Le composant enfant a son propre paramètre :topicId
qui a son propre (disons) <Route path="sections/:sectionId" component={Section} />
Route/Component.
Encore plus pénible, il n’est pas nécessaire que le menu de navigation ait une relation un-à-un avec l’arbre des composants. Parfois, les éléments situés au niveau racine du menu (par exemple, Topics
, Sections
etc.) peuvent correspondre à une structure nested (Sections
est uniquement restitué sous un sujet, /topics/:topicId/sections/:sectionId
, bien qu’elle ait sa propre liste normalisée disponible pour le serveur. sous le titre Sections dans la barre de navigation) . Par conséquent, lorsque vous cliquez sur Sections, it doit être mis en surbrillance et non les deux Sections _ etSujets.
Le chemin sectionId
ou sections
n'étant pas disponible pour le composant Barre de navigation qui se trouve au niveau racine de l'application, il devient nécessaire d'écrire des hacks comme celui-ci pour un cas d'utilisation aussi banal.
Je ne suis pas du tout un expert chez React Router. Par conséquent, si quelqu'un peut proposer une solution élégante et appropriée à ce cas d'utilisation, j'estimerais qu'il s'agit d'une tentative fructueuse. Et par élégant, je veux dire
match
et non history.location.pathname
window.location.xxx
this.props.location.pathname
path-to-regexp
Autres hacks/solutions partielles/questions connexes:
TIA!
Essayez d’utiliser les paramètres de requête ?
pour permettre au parent et à l’enfant d’accéder à la topic
actuellement sélectionnée. Malheureusement, vous devrez utiliser le module qs parce que react-router-dom
n'analyse pas automatiquement les requêtes (rea-router v3 le fait).
Exemple de travail: https://codesandbox.io/s/my1ljx40r9
L'URL est structurée comme une chaîne concaténée:
topic?topic=props-v-state
Ensuite, vous ajouteriez à la requête avec &
:
/topics/topic?topic=optimization&category=pure-components&subcategory=shouldComponentUpdate
✔ Correspondance des utilisations pour la gestion des URL de route
✔ N'utilise pas this.props.location.pathname
(utilise this.props.location.search
)
✔ Utilise qs
pour analyser location.search
✔ N'implique pas les approches de hacky
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>
);
};
Une autre approche consiste à créer une variable HOC
qui stocke les paramètres dans state
et les enfants mettent à jour la variable state
du parent lorsque ses paramètres ont été modifiés.
L'URL est structurée comme une arborescence de dossiers: /topics/rendering/optimization/pure-components/shouldComponentUpdate
Exemple de travail: https://codesandbox.io/s/9joknpm9jy
✔ Correspondance des utilisations pour la gestion des URL de route
✔ N'utilise pas this.props.location.pathname
✔ Utilise lodash pour la comparaison d'objet à objet
✔ N'implique pas les approches de hacky
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
ne vous donne pas les paramètres de correspondance de l'un des itinéraires enfants correspondants, il vous donne plutôt les paramètres en fonction de la correspondance actuelle. Donc, si vous avez configuré votre itinéraire comme
<Route path='/topic' component={Topics} />
et dans le composant Topics
vous avez un itinéraire comme
<Route path=`${match.url}/:topicId` component={Topic} />
Désormais, si votre URL est /topic/topic1
qui correspond à la Route intérieure, mais pour le composant Topics, la Route correspondante est toujours, /topic
et ne contient donc aucun paramètre, ce qui est logique.
Si vous voulez récupérer les paramètres de la route enfant correspondante dans le composant topic, vous devez utiliser l'utilitaire matchPath
fourni par React-router et tester la route enfant dont vous souhaitez obtenir les paramètres.
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>
)
}
MODIFIER:
Une méthode pour obtenir tous les paramètres à n'importe quel niveau consiste à utiliser le contexte et à mettre à jour les paramètres au fur et à mesure qu'ils correspondent dans le fournisseur de contexte.
Vous devez créer une enveloppe autour de Route pour que celle-ci fonctionne correctement. Un exemple typique ressemblerait à ceci:
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")
);
Essayez de faire quelque chose comme ça:
<Switch>
<Route path="/auth/login/:token" render={props => <Login {...this.props} {...props}/>}/>
<Route path="/auth/login" component={Login}/>
Premièrement, la route avec le paramètre et après le lien sans paramètre . À l’intérieur de mon composant Login, j’ai mis cette ligne de code console.log(props.match.params.token);
à tester et a travaillé pour moi.