我有一个显示很多产品的目录页面,一旦用户单击他可能从底部选择的产品之一,比如说产品编号 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;
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
问题
因此,应用程序中存在一些对您不利的因素。
可能的建议解决方案
为了解决状态持久性问题,这里的解决方案是将状态提升到共同祖先的模式,使其比正在渲染的路由组件持续更长时间。在父
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
为了解决需要渲染的数据量问题,您可以转向虚拟化或窗口化。
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)}>
)}
)}演示