REACT - 避免在后浏览器按钮上重新渲染页面
P粉733166744
P粉733166744 2024-03-27 14:50:53
0
1
469

我有一个显示很多产品的目录页面,一旦用户单击他可能从底部选择的产品之一,比如说产品编号 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粉733166744
P粉733166744

全部回复(1)
P粉762447363

问题

因此,应用程序中存在一些对您不利的因素。

  1. 数据被获取并存储在正在导航到的组件的本地状态中。一旦应用导航到另一条路线,数据就会丢失,并在返回时重新获取。
  2. 大量数据需要渲染。

可能的建议解决方案

为了解决状态持久性问题,这里的解决方案是将状态提升到共同祖先的模式,使其比正在渲染的路由组件持续更长时间。在父 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)}>
)}
)}
); };

演示

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板