REACT - 避免在後瀏覽器按鈕上重新渲染頁面
P粉733166744
P粉733166744 2024-03-27 14:50:53
0
1
437

我有一個顯示很多產品的目錄頁面,一旦使用者點擊他可能從底部選擇的產品之一,比如說產品編號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)}>
)}
)}
); };

演示

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板