Reputation: 67
When clicking on a specific pokemon, the user can show details of the pokemon. When the user clicks the back button to see all pokemon, I want them to continue in the exact same scroll position/pokemon, where they first clicked. How to achieve this?
Here is the pokedex component, where the user can click on each pokemon:
import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { ScrollArrow } from './utils/scrollArrow';
import { Container, Card, Col, Row, Spinner } from 'react-bootstrap';
const Pokedex = () => {
const [pokemon, setPokemon] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
getPokedex();
}, []);
const getPokedex = async () => {
try {
const res = await fetch('https://pokeapi.co/api/v2/pokemon?limit=151');
const { results } = await res.json();
const pokedex = results.map((pokemon: any, index: number) => {
const paddedId = ('00' + (index + 1)).slice(-3);
const image = `https://assets.pokemon.com/assets/cms2/img/pokedex/detail/${paddedId}.png`;
return { ...pokemon, image };
});
setPokemon(pokedex);
setLoading(false);
} catch (err) {
console.error(err);
}
};
return (
<Container fluid className='pokedex'>
{loading ? (
<Spinner animation='border' role='status'>
<span className='visually-hidden'>Fetching Pokemon...</span>
</Spinner>
) : (
<Row>
{pokemon.map((pokemon: any, index: number) => (
<Col key={index} xs={12} sm={6} lg={4} xl={2} className='col'>
<Card>
<Link to={`/pokemon/${index + 1}`}>
<Card.Img src={pokemon.image} alt={pokemon.name} />
<Card.Body>
<Card.Text>
#{(index + 1).toString().padStart(3, '0')}
</Card.Text>
<Card.Title>{pokemon.name}</Card.Title>
</Card.Body>
</Link>
</Card>
</Col>
))}
</Row>
)}
<ScrollArrow />
</Container>
);
};
export default Pokedex;
Here is the pokemon component, where the user can go back to the pokedex to see all pokemon:
import { useEffect, useState } from 'react';
import { colors } from './utils/bgColor';
import {
Button,
Col,
Container,
Image,
Row,
Spinner,
ListGroup,
ProgressBar,
Tab,
Tabs,
TabContainer,
} from 'react-bootstrap';
import { useNavigate, useParams } from 'react-router-dom';
const Pokemon = () => {
const [pokemonDetails, setPokemonDetails] = useState<any>([]);
const [loading, setLoading] = useState(true);
const { id } = useParams();
const getPokemon = async (id: string | undefined) => {
try {
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
const pokemon = await res.json();
const paddedId = ('00' + id).slice(-3);
pokemon.image = `https://assets.pokemon.com/assets/cms2/img/pokedex/detail/${paddedId}.png`;
setPokemonDetails(pokemon);
setLoading(false);
} catch (err) {
console.error(err);
}
};
useEffect(() => {
getPokemon(id);
}, [id]);
let navigate = useNavigate();
const handleClick = () => {
navigate('/');
};
let typeName = pokemonDetails.types && pokemonDetails.types[0].type.name;
const bgColor: string = colors[typeName];
return (
<Container fluid className='pokemon' style={{ backgroundColor: bgColor }}>
{loading ? (
<Spinner animation='border' role='status'>
<span className='visually-hidden'>Fetching Pokemon...</span>
</Spinner>
) : (
<div className='details' style={{ position: 'relative' }}>
<Row>
<Col className='header'>
<h1>{pokemonDetails.name}</h1>
<h3>#{pokemonDetails.id.toString().padStart(3, '0')}</h3>
</Col>
</Row>
<Row>
<Col>
<ListGroup className='type'>
{pokemonDetails.types.map((type: any, index: number) => (
<ListGroup.Item key={index}>{type.type.name}</ListGroup.Item>
))}
</ListGroup>
</Col>
</Row>
<Row>
<Image
src={pokemonDetails.image}
alt={pokemonDetails.name}
className='pokemon-img'
/>
</Row>
<TabContainer>
<Row className='clearfix'>
<Col sm={12} className='box'>
<Tabs defaultActiveKey='stats'>
<Tab eventKey='abilities' title='Abilities'>
<ListGroup>
{pokemonDetails.abilities.map(
(ability: any, index: number) => (
<ListGroup.Item key={index}>
{ability.ability.name}
</ListGroup.Item>
)
)}
</ListGroup>
</Tab>
<Tab eventKey='stats' title='Stats'>
<ListGroup>
{pokemonDetails.stats.map((stat: any, index: number) => (
<ListGroup.Item key={index}>
{stat.stat.name}
<ProgressBar
now={stat.base_stat}
label={stat.base_stat}
/>
</ListGroup.Item>
))}
</ListGroup>
</Tab>
<Tab eventKey='moves' title='Moves'>
<ListGroup className='moves'>
{pokemonDetails.moves
.slice(0, 62)
.map((move: any, index: number) => (
<ListGroup.Item key={index}>
{move.move.name}
</ListGroup.Item>
))}
</ListGroup>
</Tab>
<Tab eventKey='evolutions' title='Evolutions' disabled>
{/* <p className='possible evolution'>
{pokemonDetails.stats.map((type: any, index: number) => (
<p key={index}>{type.type.name}</p>
))}
</p> */}
</Tab>
</Tabs>
</Col>
</Row>
</TabContainer>
<Button variant='dark' onClick={handleClick}>
Catch another Pokémon
</Button>
</div>
)}
</Container>
);
};
export default Pokemon;
Upvotes: 1
Views: 12665
Reputation: 92
You can check browser history API https://developer.mozilla.org/en-US/docs/Web/API/History#specifications
Or you can get the current scroll position before navigating and then reset it when you return.
window.pageYOffset || document.documentElement.scrollTop - This should get you current scroll position
Upvotes: 0
Reputation: 1213
I found an answer in this thread: How do people handle scroll restoration with react-router v4?
this is in v4 and TS but still have a good solution for this Hopefully it will help you
Upvotes: 0