使用滚动事件问题来反应钩子的状态

问题描述 投票:0回答:1

这是我第一次使用反应钩子。我尝试使用状态(布尔值)禁用提取。但是有一个问题,因为函数仍然看到初始状态并执行函数十次而不是一次。我看不出这一点。在react控制台中,状态被标记为true,并且仍然传递我的if语句

我注意到事实,没有窗户上的监听器它正在工作。所以我尝试在React - onScroll中使用事件构建,但这不能解决我的问题 - 什么都没发生。我的React版本:16.8.6

const [isFetchedData, setIsFetchedData] = useState(false);
 const fetchBookData = async () => {
    await setisFetchedData(true);
    ... - fetchedData
    setTimeout(() => {
      setIsFetchedData(false);
    }, 5000); --- I would like to make available to fetch data again after 5 seconds.
  };

  useEffect(() => {
    window.addEventListener("scroll", scrollHandler);
  });

  const scrollHandler = () => {
    if (!isFetchedData) {
      fetchBookData(); --- always executing
    }
  };

Fleischpflanzerl - 我把这个函数放在useEffect体中。函数scrollHandler现在不执行。你能给我一些如何解决这个问题的例子吗?

useEffect(() => {
    return () => {
      window.addEventListener("scroll", scrollHandler, true);
      const scrollHandler = () => {
        if (!isFetchedData) {
          // fetchBookData();
          console.log("fetch");
        }
      };
    };
  }, [isFetchedData]);
reactjs
1个回答
1
投票

我想你应该在这种情况下使用useRef而不是useState

调用useState时有什么问题?

如果您调用setIsFetchedData来设置isFetchedData,则会触发组件中的重新渲染。因此,每次滚动时,都会触发重新渲染的组件。

The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.

主要问题是isFetchedData只是你钩子里的一个布尔值。不是一个立即改变它的价值的神奇变量。 (React只对重新渲染进行排队,它不会立即重新渲染。)

但是当滚动事件多次出现时,事件处理函数会将isFetchedData视为静态false。如果是false,你再次打电话给fetch

关键是isFetchedData不会改变,直到下一次重新渲染再次调用useState

代码中的另一个问题是,您忘了定义清理函数和依赖项。在每个渲染中,React调用useEffect,所以如果你没有返回一个清理函数并且没有定义告诉React何时运行这个效果的依赖关系,那么每次组件重新渲染并附加处理程序时都会调用addEventListener多次。

我用useRef做了一个工作的例子,也许价值超过千言万语。

const useRef = React.useRef;
const useEffect = React.useEffect;

const useScrollFetch = () => {
	const isFetchedData = useRef(false);

	function fakeFetchBookData() {
		console.log("fakeFetchBookData start...");
		setTimeout(() => {
		  console.log("fakeFetchBookData end...");
    }, 2000);
	}
	function fetchBookData() {
		isFetchedData.current = true;
		fakeFetchBookData();
		setTimeout(() => {
			console.log("isFetchedData reset");
			isFetchedData.current = false;
		}, 5000); // I would like to make available to fetch data again after 5 seconds.
	}

	const scrollHandler = event => {
		if (!isFetchedData.current) {
			console.log(
				"scrollHandler: ",
				event.type,
				"isFetchedData: ",
				isFetchedData
			);
			fetchBookData();
		}
	};
	useEffect(() => {
		window.addEventListener("scroll", scrollHandler, true);
		return () => {
			window.removeEventListener("scroll", scrollHandler, true);
		};
	}, []);
};

function App() {
  console.log("App render...");
	useScrollFetch();
	return (
		<div className="App" style={{ width: '10%' }}>
			<p>
				Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
				dictum, enim vitae porttitor vulputate, sem nulla faucibus
				felis, a faucibus ex lacus at mauris. Mauris scelerisque rhoncus
				nisi vel commodo. Suspendisse varius lectus porta lectus
				commodo, consectetur condimentum nibh euismod. Aenean cursus
				nibh erat, non rhoncus arcu porta vel. Pellentesque maximus
				mauris nec neque porttitor commodo. In condimentum hendrerit
				augue, vitae lobortis est iaculis quis. Fusce vehicula sit amet
				dui vel dignissim. Donec tempus hendrerit tristique. Nam
				suscipit aliquet nisl eu malesuada.
			</p>
		</div>
	);
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<div id="root"></div>

<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>

(我不得不删除async / await,因为我无法使用这些语法嵌入代码,但这不是问题。)

我真的建议阅读A Complete Guide to useEffect from Dan Abramov。它帮助我更好地理解useEffect的工作原理。 :)

© www.soinside.com 2019 - 2024. All rights reserved.