使用滚动事件反应挂钩状态

时间:2019-04-14 22:12:44

标签: reactjs

这是我第一次使用react hooks。 我尝试使用状态(布尔值)禁用获取。 但是有一个问题,因为一个函数仍然会看到其初始状态并执行一次而不是执行十二次。 我看不到重点。在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]);

1 个答案:

答案 0 :(得分:0)

在这种情况下,我认为您应该使用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只是您的一个布尔值。这不是一个可以立即改变其价值的神奇变量。 (仅反应排队重新渲染,它不会立即重新渲染。)

但是,当滚动事件发生多次时,事件处理函数将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/react@16.8.6/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.8.6/umd/react-dom.production.min.js"></script>

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

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