React предоставляет множество мощных инструментов, которые помогут вам создать любой веб-сайт, который вы себе представляете. Моим первым проектом на основе React была игра с набором текста. Идея заключалась в том, чтобы текст перемещался вверх по экрану, и пользователь должен был ввести его правильно, прежде чем он исчезнет с верха экрана. Как только я начал создавать основы, мне понадобился текст-заполнитель для тестирования игры, и, увидев, насколько дизайн напоминает заставку «Звездных войн», я добавил туда текст из Эпизода V. Этот временный текст быстро стал постоянным, и так родилась TypeSabers.

Проблема заключалась в том, как заставить игру завершиться, если игровой курсор пересек верхнюю часть игрового контейнера. Решение, которое я придумал, представляло собой квартет JS setInterval, хуки React useEffect и useRef и метод HTMLdom .getBoundingClientRect().

Хук useRef позволяет вам назначить ссылку на HTML-элемент, в моем случае это элементы div gameBox и cursor. Затем я мог бы вызвать для них метод .getBoundingClientRect(). Из МДН:

Ключ DOMRect, на который мне нужно было сослаться, был top, значение которого представляет собой длину между верхом элемента и верхом окна в пикселях. Важно отметить, что это значение является местоположением на экране, а не на странице, а это означает, что если пользователь прокручивает страницу вниз, значение будет уменьшаться по мере приближения элемента к верхней части окна, переходя в отрицательные значения, если он от верхней части окна. После того, как я понял, я мог написать функцию, которая возвращает логическое значение, если cursor DOMRect.top меньше, чем gameBox.

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

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

И последний совет для тех, кто впервые играет с интервалами: при создании интервала, логика которого ссылается на изменяющиеся переменные и зависит от этих обновленных значений, убедитесь, что значение хранится в объекте или массиве. Ссылаясь на контейнер, логика интервалов найдет обновленное значение внутри. Если вы передадите ему значение напрямую как просто именованную переменную, интервал будет считывать только начальное значение. Самый простой способ сделать это — сохранить динамические переменные с помощью хука useRef. useRef(value) возвращает объект {current: value}.

Если вы хотите попробовать свои силы в TypeSabers, вы можете найти это здесь. Если вы хотите заглянуть под капот, загляните в Репозиторий GitHub.