如何在React中使用Hooks实现无限滚动时防止页面重新渲染后滚动到顶部?

3

我正在尝试使用React Hooks设置无限滚动,我可以从node后端正确获取数据(每个请求3个数据集),当我滚动时,新的数据也会正确地添加到数组中(在loadedPlaces状态中),但是重新渲染时页面会回到顶部,我需要防止这种行为。如何防止这种情况发生,以下是我的代码

import React, { useEffect, useState } from "react";
import "./App.css";

function App() {
  const [page, setPage] = useState(1);
  const [loadedPlaces, setLoadedPlaces] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const getPlaces = async () => {
      try {
        setIsLoading(true);
        const url = `http://localhost:5000/api/places/user/${id}?page=${page}&size=3`;
        const responseData = await fetch(url);
        const data = await responseData.json();
        console.log(data);
        setLoadedPlaces((prev) => [...prev, ...data.places]);
        setIsLoading(false);
      } catch (error) {}
    };
    getPlaces();
  }, [page]);

  window.onscroll = function (e) {
    if (
      window.innerHeight + document.documentElement.scrollTop ===
      document.documentElement.offsetHeight
    ) {
      setPage(page + 1);
    }
  };

  return (
    <div className='App'>
      <h1>Infinite Scroll</h1>
      {!isLoading &&
        loadedPlaces.length > 0 &&
        loadedPlaces.map((place, index) => (
          <div key={index} className={"container"}>
            <h1>{place.location.lat}</h1>
            <h1>{place.location.lng}</h1>
            <h1>{place.title}</h1>
            <h1>{place.description}</h1>
            <h1>{place.address}</h1>
            <hr></hr>
          </div>
        ))}
    </div>
  );
}

export default App;

非常感谢您的帮助。
2个回答

7
由于每次滚动页面时都会调用,所以出现了这种情况。
window.onscroll = function (e) {
    if (
      window.innerHeight + document.documentElement.scrollTop ===
      document.documentElement.offsetHeight
    ) {
      setPage(page + 1);
    }
  };

这将改变页面计数,进而导致再次运行

 useEffect(() => {
    const getPlaces = async () => {
      try {
        setIsLoading(true);
        const url = `http://localhost:5000/api/places/user/${id}?page=${page}&size=3`;
        const responseData = await fetch(url);
        const data = await responseData.json();
        console.log(data);
        setLoadedPlaces((prev) => [...prev, ...data.places]);
        setIsLoading(false);
      } catch (error) {}
    };
    getPlaces();
  }, [page]);

在这个函数中,您正在执行 setIsLoading(true),以便再次呈现此内容,因为……

{!isLoading && <-----
        loadedPlaces.length > 0 &&
        loadedPlaces.map((place, index) => (
          <div key={index} className={"container"}>
            <h1>{place.location.lat}</h1>
            <h1>{place.location.lng}</h1>
            <h1>{place.title}</h1>
            <h1>{place.description}</h1>
            <h1>{place.address}</h1>
            <hr></hr>
          </div>
        ))}

这会将你带到页面的顶部

你可以尝试这个方法:

import React, { useEffect, useState } from "react";
import "./App.css";

function App() {
  const [page, setPage] = useState(1);
  const [loadedPlaces, setLoadedPlaces] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const getPlaces = async () => {
      try {
        setIsLoading(true);
        const url = `http://localhost:5000/api/places/user/${id}?page=${page}&size=3`;
        const responseData = await fetch(url);
        const data = await responseData.json();
        console.log(data);
        setLoadedPlaces((prev) => [...prev, ...data.places]);
        setIsLoading(false);
      } catch (error) {}
    };
    getPlaces();
  }, [page]);

  window.onscroll = function (e) {
    if (
      window.innerHeight + document.documentElement.scrollTop ===
      document.documentElement.offsetHeight
    ) {
      setPage(page + 1);
    }
  };

  return (
    <div className='App'>
      <h1>Infinite Scroll</h1>
       {
        loadedPlaces.length > 0 &&
        loadedPlaces.map((place, index) => (
          <div key={index} className={"container"}>
            <h1>{place.location.lat}</h1>
            <h1>{place.location.lng}</h1>
            <h1>{place.title}</h1>
            <h1>{place.description}</h1>
            <h1>{place.address}</h1>
            <hr></hr>
          </div>
        ))}
    </div>
  );
}

export default App;

哇,只需如此小的更改,它就完美地按照我的需求工作了。还有一个问题,如果后端没有更多数据传来,React应该如何处理?只是大致想法即可。 - gopinath krm
1
所以,如果列表中没有更多数据了,那么你将会得到一个空的data.places,因此你可以根据这个条件来管理没有更多的数据。如果之前的回答对你有帮助,请考虑批准该回答。谢谢! - Sanket Shah
1
哇,我正在寻找同样的东西,而且我距离添加无限滚动包到我的应用程序以避免这种情况只有一个点击之遥。幸运的是,我找到了这个解决方案,现在我被拯救了!谢谢 :) - Helen
1
多好的解决方案啊! - Alexander Donets

0

你可以添加这个。

function ScrollToBottom(){
  const elementRef = useRef();
  useEffect(() => elementRef.current.scrollIntoView());
  return <div ref={elementRef} />;
};

然后:

return (
    <div className='App'>
      <h1>Infinite Scroll</h1>
      {!isLoading &&
        loadedPlaces.length > 0 &&
        loadedPlaces.map((place, index) => (
          <div key={index} className={"container"}>
            <h1>{place.location.lat}</h1>
            <h1>{place.location.lng}</h1>
            <h1>{place.title}</h1>
            <h1>{place.description}</h1>
            <h1>{place.address}</h1>
            <hr></hr>
          </div>
        ))}
      <ScrollToBottom /> 
    </div>
  );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接