搜索

首页  >  问答  >  正文

REACT - 避免在后浏览器按钮上重新渲染页面

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

全部回复(1)我来回复

  • P粉762447363

    P粉7624473632024-03-28 10:38:14

    问题

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

    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
    // <-- 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)}>
    )}
    )}
    ); };

    演示

    回复
    0
  • 取消回复