Проблема с состоянием перехвата с использованием событий прокрутки

#reactjs

#reactjs

Вопрос:

Это первый раз, когда я использую перехваты 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. Похоже, вам нужно поместить scrollHandler тело внутри функции, которую вы передаете в useEffect hook. Проверьте эту проблему, она как раз об этом (плюс второй аргумент для useEffect, он же deps): github.com/facebook/react/issues/14920 .

2. @Fleischpflanzerl — по-прежнему не работает: (Я вставляю свой код в основной пост

Ответ №1:

Я думаю, вам следует использовать useRef вместо useState в этой ситуации.

Что не так, когда вы вызываете useState?

Если вы вызываете setIsFetchedData set isFetchedData , вы запускаете повторную визуализацию в своем компоненте. Таким образом, каждый раз, когда вы прокручиваете, вы запускаете повторный рендеринг компонента.

Для обновления состояния используется функция setState. Он принимает новое значение состояния и ставит в очередь повторную визуализацию компонента.

Основная проблема заключается в том, что 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/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, потому что я не могу встроить сюда код с этим синтаксисом, но это не проблема.)

Я действительно предлагаю прочитать Полное руководство по useEffect от Дэна Абрамова. Это очень помогло мне лучше понять, как работает useEffect. 🙂