我有一个显示很多产品的目录页面,一旦用户单击他可能从底部选择的产品之一,比如说产品编号 1000,他就会转到另一个页面,检查内容等...并且当他使用后退按钮浏览器返回目录页面时,所有内容都会再次呈现,这是有道理的,但需要花费大量时间将滚动条定位到先前选择的产品。
当用户在页面顶部选择某个产品(例如产品号 4)时,一切都运行良好,但当他转到底部时,场景就开始了。
有什么办法可以在 REACT 中缓存这个目录页面吗?为了避免渲染所需的时间?
目录页面底部的分页解决了此问题,但我有一个“加载更多”按钮。我尝试使用 React.memo,但它仅在我在当前页面上执行操作时有效,而不是在我使用后退按钮登陆页面时有效。
我正在使用React Router v5。我可以在此处添加一些带有代码的代码笔,但首先我想知道这是否可行,以便采取一些方向。我看到页面甚至需要在使用后退按钮返回后渲染所有内容,看起来像静态页面,并且滚动甚至不会移动到最后选择的产品的位置。黑暗中还有光吗?
这是我的例子。
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;
P粉7624473632024-03-28 10:38:14
因此,应用程序中存在一些对您不利的因素。
为了解决状态持久性问题,这里的解决方案是将状态提升到共同祖先的模式,使其比正在渲染的路由组件持续更长时间。在父 App
组件中或自定义 React Context 提供程序组件就足够了。
示例:
import { createContext, useContext, useEffect, useState } from "react"; const CardDataContext = createContext({ cards: [], }); const useCardData = () => useContext(CardDataContext); const CardDataProvider = ({ children }) => { const [cards, setCards] = useState([]); const fetchData = () => { fetch("https://jsonplaceholder.typicode.com/photos") .then((response) => response.json()) .then((json) => setCards(json)); }; useEffect(() => { fetchData(); }, []); return ({children} ); };
export default function App() { return (); }home
page 1
page 2
// <-- card data provided to all routes
为了解决需要渲染的数据量问题,您可以转向虚拟化或窗口化。 react-window
就是处理这个问题的一个。其作用是,它不会将整个数据数组(可能有数千个元素)渲染到 DOM,而是仅渲染适合屏幕的内容以及前后的一些“过度扫描”。
示例:
import { FixedSizeList as List } from "react-window"; import AutoSizer from "react-virtualized-auto-sizer"; const Page1 = () => { const { cards } = useCardData(); return (); };{({ height, width }) => ( {({ index, style }) => (
)})}
最后要解决的一件事是滚动恢复到特定元素。 react-window
能够滚动到特定索引。我们可以更新 CardDataContext
以保持某些滚动状态,并更新 Page1
组件以设置和恢复位置。
const CardDataContext = createContext({ cards: [], scrollIndex: null, setScrollIndex: () => {} }); ... const CardDataProvider = ({ children }) => { ... const [scrollIndex, setScrollIndex] = useState(null); ... return ({children} ); };
const Page1 = () => { const { cards, scrollIndex, setScrollIndex } = useCardData(); const listRef = useRef(); useEffect(() => { if (scrollIndex) { setTimeout(() => { listRef.current.scrollToItem(scrollIndex, "center"); }); } }, [scrollIndex]); return (); };{({ height, width }) => ( {({ index, style }) => (
)}setScrollIndex(index)}>)}