React useState не обновляется в событиях окна - PullRequest
2 голосов
/ 05 марта 2020

Состояние устанавливается на прокрутке, но при регистрации в EventListener оно кажется застрявшим на начальном значении.

Я думаю, это как-то связано с установкой scrolling, когда определен побочный эффект , но как я мог вызвать изменение состояния от прокрутки в противном случае? То же самое касается любого события окна, которое я предполагаю.

Вот пример кода и коробки: https://codesandbox.io/s/react-test-zft3e

  const [scrolling, setScrolling] = useState(false);

  useEffect(() => {
    window.addEventListener("scroll", () => {
      console.log(scrolling);
      if (scrolling === false) setScrolling(true);
    });
  }, []);

  return (
    <>
      scrolling: {scrolling}
    </>
  );

Ответы [ 2 ]

1 голос
/ 05 марта 2020

Обратный вызов прослушивателя событий инициализируется только один раз

Это означает, что переменная в этот момент также «перехватывается» в этой точке, поскольку при повторном рендеринге вы не инициализируете прослушиватель событий.

Это похоже на моментальный момент включения.

Если вы переместите console.log за пределы, вы увидите, что он изменится, когда произойдет повторное рендеринг, и снова установите значение прокрутки.

  const [scrolling, setScrolling] = useState(false);

  useEffect(() => {
    window.addEventListener("scroll", () => {
      if (scrolling === false) setScrolling(true);
    });
  }, []);

  console.log(scrolling);

  return (
    <>
      scrolling: {scrolling}
    </>
  );
0 голосов
/ 05 марта 2020

Таким образом, ваша анонимная функция заблокирована на начальном значении scrolling. Это то, как замыкания работают в JS, и вам лучше узнать какую-нибудь симпатичную статью об этом, это может быть сложно, и хуки сильно полагаются на замыкания.

Пока есть 3 различных Решения здесь:

1. Пересоздайте и перерегистрируйте обработчик для каждого изменения

useEffect(() => {
    const scrollHandler = () => {
      if (scrolling === false) setScrolling(true);
    };
    window.addEventListener("scroll", scrollHandler);
    return () => window.removeEventListener("scroll", scrollHandler);
  }, [scrolling]);

, следуя этому пути, убедитесь, что вы возвращаете функцию очистки из useEffect. Это хороший подход по умолчанию, но для его прокрутки может повлиять на производительность, поскольку событие прокрутки часто вызывает слишком .

2. Доступ к данным по ссылке

const scrolling = useRef(false);

  useEffect(() => {
    const handler = () => {
      if (scrolling.current === false) scrolling.current = true;
    };
    window.addEventListener("scroll", handler);
    return () => window.removeEventListener("scroll", handler);
  }, []);

  return (
    <>
      scrolling: {scrolling}
    </>
  );

недостаток: изменение ссылки не вызывает повторного рендеринга. Поэтому вам нужно иметь какую-то другую переменную, чтобы изменить ее, вызывая повторную визуализацию.

3. Используйте функциональную версию сеттера для доступа к самому последнему значению

(я вижу это как предпочтительный способ здесь):

useEffect(() => {
    const scrollHandler = () => {
      setScrolling((currentScrolling) => {
        if (!currentScrolling) return true;
        return false;
      });
    };
    window.addEventListener("scroll", scrollHandler);
    return () => window.removeEventListener("scroll", scrollHandler);
}, []);

Примечание Кстати, даже для эффекта одноразового использования, вам лучше вернуть функцию очистки в любом случае .

PS Также сейчас вы не устанавливаете scrolling в false, так что вы можете просто избавиться от условия if(scrolling === false), но в сценарии реального мира вы можете и столкнуться с чем-то похожим.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...