I have a catalog page that shows a lot of products, once the user clicks on one of the products he may choose from the bottom, say product number 1000, he goes to another page, checks the content etc... and when When he uses the back button browser to return to the catalog page, everything renders again, which makes sense, but takes a lot of time to position the scroll bar to the previously selected product.
Everything works fine when the user selects a certain product at the top of the page (e.g. product number 4), but when he goes to the bottom, the scenario starts.
Is there any way to cache this directory page in REACT? To avoid the time it takes to render?
Pagination at the bottom of the table of contents page solves this problem, but I have a "load more" button. I tried using React.memo but it only works when I perform an action on the current page, not when I land on the page using the back button.
I'm using React Router v5. I could add some codepen with the code here, but first I'd like to know if this is possible so I can take some direction. I've seen pages that even need to render everything after going back with the back button, look like a static page, and the scrolling doesn't even move to where the last product was selected. Is there light in the darkness?
This is my example.
App.jsx
import React, { StrictMode } from 'react'; import { BrowserRouter, Switch, Route, Link } from 'react-router-dom' import './App.css'; import Home from './Home'; import Page1 from './Page1'; import Page2 from './Page2'; function App() { return ( <StrictMode> <BrowserRouter> <div className="App"> <header className="App-header"> <Link to='/'>home</Link><br /> <Link to='/page1'>page 1</Link><br /> <Link to='/page2'>page 2</Link><br /> </header> <Switch> <Route path='/page1'> <Page1 /> </Route> <Route path='/page2'> <Page2 /> </Route> <Route path='/'> <Home /> </Route> </Switch> </div> </BrowserRouter> </StrictMode> ); } export default App;
Home.jsx
import React from 'react'; const Home = () => <h1>This is the home</h1> export default Home;
Page1.jsx
import React, { useEffect, useState } from 'react'; import Card from './Card'; const Page1 = () => { const [cards, setCards] = useState([]); const fetchData = () => { fetch('https://jsonplaceholder.typicode.com/photos') .then((response) => response.json()) .then((json) => setCards(json)) .then(() => scrollToElement()); } const scrollToElement = () => { const id = localStorage.getItem('idRef'); if (id) { var access = document.getElementById(id); access.scrollIntoView(); } } useEffect(() => { fetchData() }, []) return ( <div className="grid"> {cards.map((item) => <Card item={item} key={item.id}/>)} </div> ) } export default Page1;
Page2.jsx
import React from 'react'; const Page2 = () => <h1>Product selected... now click back browser button</h1> export default Page2;
question
So there is something working against you in the application.
Possible Suggested Solutions
To solve the state persistence problem, the solution here is to promote the state to a common ancestor so that it lasts longer than the routed component being rendered. In the parent
App
component or a custom React Context provider component is enough.Example:
To solve the problem of the amount of data that needs to be rendered, you can turn to virtualization or windowing.
react-window
is the one that deals with this problem. What this does is, it doesn't render the entire array of data (potentially thousands of elements) to the DOM, but only renders what fits on the screen with some "overscanning" before and after.Example:
One last thing to fix is scrolling back to a specific element.
react-window
Ability to scroll to a specific index. We can update theCardDataContext
to maintain some scroll state, and update thePage1
component to set and restore the position.Demo