Долгий путь к ResizeObserver. CSS.. CSS. HTML.. CSS. HTML. JavaScript.. CSS. HTML. JavaScript. react.. CSS. HTML. JavaScript. react. web api.. CSS. HTML. JavaScript. react. web api. динамические блоки.. CSS. HTML. JavaScript. react. web api. динамические блоки. ИИ.. CSS. HTML. JavaScript. react. web api. динамические блоки. ИИ. искусственный интеллект.. CSS. HTML. JavaScript. react. web api. динамические блоки. ИИ. искусственный интеллект. поиск решений.

Меня зовут Анна, я JS-разработчик в компании SimbirSoft и занимаюсь разработкой веб-приложений на React. Эту статью я посвящаю тем, кто занимается разработкой, сталкивается с нестандартными задачами и переживает, что нашу профессию может вскоре заменить искусственный интеллект (ИИ). Я поделюсь решением задачи, связанной с динамическими размерами блока, — проблемой, с которой наверняка может столкнуться в своей работе любой frontend-разработчик.

Почему же я назвала статью именно «Долгий путь к ResizeObserver»?

Возможно, я и слышала раньше про этот API, но когда передо мной встала конкретная задача (описанная ниже), я о нем даже не вспомнила. Мне пришлось пробовать сначала одно решение, потом другое — и лишь в третью очередь я пришла к нужному инструменту. Таков мой путь — из трех шагов. Я человек, поэтому могу честно рассказать, как именно искала решения, в отличие от ИИ. Надеюсь, моя статья поможет вам быстро и эффективно справиться с похожей задачей, а заодно придаст уверенности в собственных силах. Я убеждена: ответы на вопросы находятся не только в нашей голове, но и в окружающем мире, а человек, в отличие от ИИ, способен чувствовать, искать и находить их самым неожиданным для себя образом.

Итак, приступим 🙂

Перед нами лейаут, который состоит из трех блоков:

Долгий путь к ResizeObserver - 1

Высота первого и второго блоков может меняться, пока пользователь взаимодействует со страницей, а высота третьего должна подстраиваться динамически. Если высота второго блока небольшая, тогда третий блок будет иметь фиксированную высоту (200 px). Все три блока состоят из разных частей, данные в которые поступают изолированно.

Основная задача состоит в том, чтобы отслеживать высоту первого и второго блоков и, в зависимости от их значений, задавать высоту третьему блоку. Задавать ее нужно обязательно (и здесь лучше не полагаться на вычисление CSS), поскольку в третьем блоке несколько табов с контентом разной высоты, и при переключении между ними высота не должна «прыгать». Высота третьего блока не может быть меньше 200 px.

Итак, у моей статьи две цели:

  1. Рассказать, каким способом решила эту задачу — может быть полезно тем, кто столкнется с подобной ситуацией. 

  2. Рассказать, как именно я решала эту задачу — то есть описать процесс поиска решения с помощью естественного интеллекта (части статьи, посвященные этой цели, будут выделены курсивом). 

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

// создаем функцию “получить высоту табов”
export const getHeightTabs = () => {

// с помощью нативного js получаем высоты блоков 1 и 2 (чтобы это работало, // надо задать id для этих блоков ‘block1’ и ‘block2’ соответственно)
 const heightBlock1 = document.getElementById('block1')?.clientHeight || 0;
 const heightBlock2 = document.getElementById('block2')?.clientHeight || 0;

// вычисляем высоту 3-го блока: если разница между 2-ым и 1-ым блоком 
// больше 200px, то 3-ий блок равен разнице, иначе - 200px
 const heightTabs = heightBlock2 - heightBlock1 >= 200 
                    ? heightBlock2 - heightBlock1 : 200;
 return heightTabs;
};

Вызываем эту функцию при загрузке страницы, присваиваем высоте третьего блока полученное значение.

В моей ситуации такой подход не работал с самого начала, т.к. контент первого и второго блоков загружается постепенно и зависит от множества данных. Я не придумала, как вызвать эту функцию после окончательной загрузки всех данных – это первая проблема. Вторая связана с обновлением высоты: пользователь может изменять высоту первого и второго блоков, взаимодействуя с разными частями приложения (а их 10 или 12). Поэтому вызывать функцию после обновления каждой из этих частей выглядит излишне громоздким решением. 

Эта задача находилась в бэклоге: хотя «прыгающая» высота табов раздражала и меня, и дизайнера, высокой приоритетности ей никто не присваивал — было много других проблем. Поэтому я отложила ее решение и на следующий день взялась за другую, более крупную и не менее важную задачу — изменение дизайна другой части нашего приложения (приложение большое, существует уже 7 лет, и мы его не переписываем с нуля, а поддерживаем и постепенно обновляем по частям).

Тут наступает момент моей истории, где я покажу, в чем человек превосходит ИИ, ведь я собиралась утешить тех, кто тревожится, что ИИ сможет заменить разработчиков 🙂

Открываю одну из папок нашего легаси и что я там вижу?

 import {useResizeDetector} from 'react-resize-detector';

Как потом выяснилось, это единственное место в нашем приложении, где использовалась эта либа. И я наткнулась на него на следующий день после того, как предприняла свою первую попытку решить проблему с высотой табов! Для меня, как для человека, это стало чудесным знаком от окружающей реальности — указанием на то, в каком направлении нужно двигаться. То есть решение «пришло» извне, а не из моих знаний. И я абсолютно уверена: человек способен находить ответы в самых неожиданных местах. А ИИ — лишь в пределах своей обучающей базы. Никто нас не заменит 🙂

Чудесная часть моей статьи завершена — дальше будет скучнее, но, возможно, кому-то пригодится 🙂

Второй способ, который я попробовала, написала кастомный хук с использованием библиотеки react-resize-detector:

// создаем хук “определитель высоты табов”
export const useTabsHeightDetect = () => {

// создаем хранилища состояний для высот 1-го и 2-го блоков
  const [hb1, setHB1] = useState(0);
  const [hb2, setHB2] = useState(0);

// создаем колбэк-функции onResize для хука useResizeDetector, где получаем
// значение высоты нужного блока и сохраняем его в хранилище
  const onResizeBlock1 = (payload) => {
const {height} = payload;
setHB1(height);
  };
  const onResizeBlock2 = (payload) => {
	const {height} = payload;
	setHB2(height);
  };

// посредством хука useResizeDetector получаем ссылку на блок,
// к которому привязано отслеживание высоты и соответствующий колбэк
  const {ref: block1Ref} = useResizeDetector({onResize: onResizeBlock1});
  const {ref: block2Ref} = useResizeDetector({onResize: onResizeBlock2});

// возвращаем обе ссылки, которые раздадим нужным блокам, и высоты блоков
// для расчета высоты третьего
  return {block1Ref , block2Ref , hb1, hb2}
}

Ссылки на первый и второй блоки раздаем в родительском компоненте, а в компоненте табов (третьем блоке) мы получаем их высоты и рассчитываем нужную.

Почему-то информация о высоте блоков не обновлялась сразу, а только при следующем рендере. Напишите в комментариях ниже почему, если знаете. 

Я попыталась разобраться, но раньше чем успела это сделать, наткнулась на замечательную статью про ResizeObserver, которая решила мою задачу так, что либа react-resize-detector мне больше не понадобилась. Внимательный читатель наверняка понял, что я снова намекаю на основную мысль своей статьи: решения приходят к человеку разными путями, и сложность этих путей, на мой взгляд, все же выше, чем у любой искусственной нейросети 🙂

В начале статьи авторы оптимистично дают гарантии, что после прочтения мы будем иметь четкое представление о том, как создавать отзывчивые и адаптивные веб-интерфейсы с помощью ResizeObserver API. Лично со мной так не работает, я не буду иметь четкое представление о чем-нибудь после прочтения статьи. Если я просто прочитаю статью, даже если все пойму, информация испарится из головы через 15–20 минут 🙂 Но простим авторам такой маркетинговый ход — это их работа, зато пишут отличные статьи 🙂

Далее нам объясняют, что ResizeObserver — это JavaScript API, которое, как следует из названия, «наблюдает» за изменением размеров элементов веб-страницы. В статье его сравнивают с тренером, который наблюдает за командой на поле и может заметить даже усталость игрока или его возможные травмы. На мой взгляд, аналогия не совсем удачная — я бы не стала сравнивать API с тренером. Observer — это все-таки обозреватель, хотя, возможно, имелось в виду, что никто не следит за игроками так внимательно, как тренер, а наше API такое же внимательное и заботливое. Может быть, но мне кажется, что это не совсем верно и довольно абстрактное сравнение.  Тем не менее, заметив изменения элемента, ResizeObserver может вызвать колбэк, который разработчик может задать.

Далее в статье приводится сравнение ResizeObserver с медиа- и контейнер-запросами, которые разработчики обычно применяют для создания адаптивного дизайна, затем перечисляются ключевые преимущества использования ResizeObserver:

  • Мониторинг элементов: в отличие от resize events, которое отслеживает изменение размеров всего окна, ResizeObserver фокусируется на конкретных элементах страницы.

  • Улучшение производительности: позволяет избежать излишних дорогостоящих вычислений.

  • Динамическая адаптация контента: ResizeObserver — бесценный инструмент для задач, где верстка или функциональность зависят от размеров элементов, например динамические гриды, коллапсирующий сайдбар или адаптивные шрифты.

На основе приведенных примеров использования ResizeObserver я и написала хук, который отлично решил мою задачу:

// создаем хук “наблюдатель за высотой табов”, который получает ссылки
// на блоки, за которыми будет наблюдать 
export const useTabsHeightObserver = (refs) => {
// создаем хранилища для высот 1-го и 2-го блоков
  const [hb1, setHB1] = useState(0);
  const [hb2, setHB2] = useState(0);    
  // вызываем useLayoutEffect для отслеживания изменений до того,
  // как браузер перерисует экран
  useLayoutEffect(() => {
	// декомпозируем аргумент хука, получаем ссылки на блоки
        const { block1Ref, block2Ref } = refs;
	// получаем элементы из DOM за которыми наблюдаем
        const observeBlock1 = block1Ref.current;
        const observeBlock2 = block2Ref.current;
	// если элементов нет, то прекращаем наблюдение
        if (!observeBlock1 || !observeBlock2) return;
	// создаем экземпляр ResizeObserver - 
// “наблюдателя” за конкретным блоком, передаем ему колбэк для вызова
// при изменении размеров блока (сохраняем высоту блока в хранилище)
        const resizeObserverBlock1 = new ResizeObserver((entries) => {
            for (let entry of entries) {
                const { height } = entry.contentRect;
                setHB1(height);
            }
        });
        const resizeObserverBlock2 = new ResizeObserver((entries) => {
            for (let entry of entries) {
                const { height } = entry.contentRect;
                setHB2(height);
            }
        });
	// призываем наблюдателей наблюдать за конкретными элементами
        resizeObserverBlock1.observe(observeBlock1);
        resizeObserverBlock2.observe(observeBlock2);
	// увольняем наблюдателей после исчезновения элементов наблюдения
        return () => {
            resizeObserverBlock1.unobserve(observeBlock1);
            resizeObserverBlock2.unobserve(observeBlock2);
        };
    }, [refs]);
  // вычисляем и возвращаем высоту третьего блока
  const tabsHeight = hb2 - hb1;
  return tabsHeight;
};

Увидеть работу моего хука можно в песочнице.

Заключение

В заключение хочу еще раз подчеркнуть основную идею своей статьи. Конечно, мне хотелось поделиться тем, какой замечательный хук я написала, вдохновившись ResizeObserver. Но за этим стоит нечто более важное. Когда мы погружаемся в обдумывание решения той или иной задачи, то оно уже существует в окружающем нас мире — в том, что мы видим ежедневно, просто живя своей обычной жизнью. Поэтому если перед вами стоит задача, просто дайте себе время подумать и оглянитесь вокруг. Решение рядом. Мы лучше ИИ, поскольку наша база данных — это весь мир 🤩 Так мы победим 🙂

Очень жду ваших комментариев. Спасибо за внимание

Больше авторских материалов для frontend-разработчиков от моих коллег читайте в соцсетях SimbirSoft – ВКонтакте и Telegram.

Автор: SimbirSoft_frontend

Источник

Rambler's Top100