J'en ai deux <Route>
s créés avec react-router.
Lorsque l'utilisateur clique sur "Retour à la liste", je souhaite faire défiler l'utilisateur là où il se trouvait dans la liste.
Comment puis-je faire ceci?
Exemple de travail à codesandbox
React Router v4 ne fournit pas de support prêt à l'emploi pour la restauration de défilement et tel qu'il est actuellement, ils ne le seront pas non plus. Dans la section React Router V4 - Scroll Restoration de leurs documents, vous pouvez en savoir plus à ce sujet.
Donc, il appartient à chaque développeur d'écrire une logique pour prendre en charge cela, bien que nous ayons quelques outils pour faire fonctionner cela.
.scrollIntoView()
peut être appelée sur un élément et comme vous pouvez le deviner, il le fait défiler dans la vue. Le support est assez bon, actuellement, 97% des navigateurs le supportent. Source: icanuse
<Link />
Peut transmettre l'étatLe composant Link de React Router a un accessoire to
que vous pouvez fournir un objet au lieu d'une chaîne. Voici à qui cela ressemble.
<Link to={{ pathname: '/card', state: 9 }}>Card nine</Link>
Nous pouvons utiliser state pour transmettre des informations au composant qui sera rendu. Dans cet exemple, un numéro est attribué à l'état, ce qui suffira pour répondre à votre question, vous verrez plus tard, mais cela peut être n'importe quoi. La route /card
Rendant <Card />
Aura désormais accès à l'état variable dans props.location.state et nous pouvons utilisez-le comme nous le souhaitons.
Lors du rendu des différentes cartes, nous ajoutons une classe unique à chacune. De cette façon, nous avons un identifiant que nous pouvons transmettre et savons que cet élément doit être défilé dans la vue lorsque nous revenons à l'aperçu de la liste des cartes.
<Cards />
Affiche une liste, chaque élément avec une classe unique;Link />
Transmet l'identifiant unique à <Card />
;<Card />
Affiche les détails de la carte et un bouton de retour avec l'identifiant unique;<Cards />
Est monté, .scrollIntoView()
fait défiler jusqu'à l'élément sur lequel vous avez cliqué précédemment en utilisant les données de props.location.state
.Voici quelques extraits de code de différentes parties.
// Cards component displaying the list of available cards.
// Link's to prop is passed an object where state is set to the unique id.
class Cards extends React.Component {
componentDidMount() {
const item = document.querySelector(
".restore-" + this.props.location.state
);
if (item) {
item.scrollIntoView();
}
}
render() {
const cardKeys = Object.keys(cardData);
return (
<ul className="scroll-list">
{cardKeys.map(id => {
return (
<Link
to={{ pathname: `/cards/${id}`, state: id }}
className={`card-wrapper restore-${id}`}
>
{cardData[id].name}
</Link>
);
})}
</ul>
);
}
}
// Card compoment. Link compoment passes state back to cards compoment
const Card = props => {
const { id } = props.match.params;
return (
<div className="card-details">
<h2>{cardData[id].name}</h2>
<img alt={cardData[id].name} src={cardData[id].image} />
<p>
{cardData[id].description} <a href={cardData[id].url}>More...</a>
</p>
<Link
to={{
pathname: "/cards",
state: props.location.state
}}
>
<button>Return to list</button>
</Link>
</div>
);
};
// App router compoment.
function App() {
return (
<div className="App">
<Router>
<div>
<Route exact path="/cards" component={Cards} />
<Route path="/cards/:id" component={Card} />
</div>
</Router>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Une autre solution possible est de rendre votre /cards/:id
route en tant que modal plein écran et conservez le /cards
route montée derrière
Pour l'implémentation complète en utilisant Redux, vous pouvez le voir sur CodeSandbox .
Je l'ai fait en utilisant l'API historique.
Enregistrer la position de défilement après un changement d'itinéraire.
Restaurez la position de défilement lorsque l'utilisateur clique sur le bouton de retour.
Enregistrez la position de défilement dans getSnapshotBeforeUpdate
et restaurez-la dans componentDidUpdate
.
// Saving scroll position.
getSnapshotBeforeUpdate(prevProps) {
const {
history: { action },
location: { pathname }
} = prevProps;
if (action !== "POP") {
scrollData = { ...scrollData, [pathname]: window.pageYOffset };
}
return null;
}
// Restore scroll position.
componentDidUpdate() {
const {
history: { action },
location: { pathname }
} = this.props;
if (action === "POP") {
if (scrollData[pathname]) {
setTimeout(() =>
window.scrollTo({
left: 0,
top: scrollData[pathname],
behavior: "smooth"
})
);
} else {
setTimeout(window.scrollTo({ left: 0, top: 0 }));
}
} else {
setTimeout(window.scrollTo({ left: 0, top: 0 }));
}
}