Как наблюдать, когда изменение размера окна останавливается в React

Я хочу наблюдать, когда размер окна перестанет изменяться, потому что в настоящее время, если я слушаю событие resize в window, я могу только слушать изменение размера change, но не знаю, когда изменение размера прекращается. Я проверил параметр event в обратном вызове изменения размера, но не нашел полезной информации.

Ниже приведен код, который я пытаюсь выполнить:

import React, { useState, useEffect } from "react";

export default function App() {
  const [windowResizing, setWindowResizing] = useState(false);

  const handleWindowResize = e => {
    setWindowResizing(true);
  };

  useEffect(() => {
    window.addEventListener("resize", handleWindowResize);

    return () => window.removeEventListener("resize", handleWindowResize);
  }, []);

  return <div>{JSON.stringify({ windowResizing })}</div>;
}

Но это не работает, так как windowResizing будет оставаться true после начала изменения размера, даже если я уже перестал изменять размер.

Итак, есть ли способ наблюдать, когда размер окна перестает изменяться, и я могу затем вызвать setState на основе этого?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
3 067
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В браузере нет реального «состояния изменения размера»; происходит событие resize, а затем оно заканчивается.

Вы можете эмулировать собственное состояние изменения размера, установив для переменной состояния значение true для каждого события resize и запустив новый тайм-аут, который сбросит его обратно на false через небольшой промежуток времени.

Пример

import React, { useState, useEffect } from "react";

export default function App() {
  const [windowResizing, setWindowResizing] = useState(false);

  useEffect(() => {
    let timeout;
    const handleResize = () => {
      clearTimeout(timeout);

      setWindowResizing(true);

      timeout = setTimeout(() => {
        setWindowResizing(false);
      }, 200);
    }
    window.addEventListener("resize", handleResize);

    return () => window.removeEventListener("resize", handleResize);
  }, []);

  return <div>{JSON.stringify({ windowResizing })}</div>;
}

Ура, это именно то, что я хочу, однако у меня есть один небольшой вопрос: в чем разница между созданием timeout в качестве ссылки и просто помещением его в качестве локальной переменной в useEffect?

Limboer 22.12.2020 05:03

@Лимбур Отлично! На самом деле это просто вопрос предпочтений. Поскольку переменная тайм-аута была нужна только в эффекте, я поместил ее как обычную переменную области видимости. Если бы вы хотели иметь возможность очистить тайм-аут, например. обработчик событий, ref был бы более подходящим.

Tholle 22.12.2020 05:06

Спасибо за ваше подробное объяснение. Последний вопрос: я вижу, вы вставили handleResize() внутрь useEffect, есть ли какая-то особая причина для этого? Или это просто вопрос предпочтения кодирования? Обычно я предпочитаю помещать функцию или метод независимо друг от друга внутри компонента, чтобы иметь возможность ссылаться на него в других местах.

Limboer 22.12.2020 07:50

@Limboer Это тоже в основном предпочтение. Поскольку мы передаем пустой массив в качестве второго аргумента эффекта, он будет запущен только один раз после первоначального рендеринга. Поместив handleResize в эффект, становится ясно, что одна и та же функция будет использоваться для addEventListener и removeEventListener. Та же функция будет использоваться, даже если мы поместим ее вне эффекта, но это не так очевидно. Кратко это обсуждается в FAQ в документации, поэтому я и поставил функции в эффект.

Tholle 22.12.2020 08:04

Небольшое обновление к ответу Толле

чтобы включить размер и состояние изменения размера

import { useState, useLayoutEffect } from 'react'

// https://stackoverflow.com/a/63010184
function debounce(fn, ms) {
    let timer;
    return _ => {
        clearTimeout(timer);
        timer = setTimeout(_ => {
            timer = null;
            fn.apply(this, arguments);
        }, ms);
    };
}

const useWindowSize = () => {
    const [size, setSize] = useState([0, 0, false]);
    useLayoutEffect(() => {
        let timeout;
        clearTimeout(timeout);
        function updateSize() {
            clearTimeout(timeout);
            setSize([window.innerWidth, window.innerHeight, true]);
            timeout = setTimeout(() => {
                setSize([window.innerWidth, window.innerHeight, false]);
            }, 800);
        }
        const debouncedResizeHandler = debounce(() => updateSize())
        window.addEventListener('resize', debouncedResizeHandler);
        setSize([window.innerWidth, window.innerHeight, false]);
        return () => window.removeEventListener('resize', debouncedResizeHandler);
    }, []);
    return size;
}

export default useWindowSize

Другие вопросы по теме