首頁  >  問答  >  主體

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粉733166744205 天前404

全部回覆(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
  • 取消回覆