Визуализация вероятностных экспериментов с хостингом React, TailwindCSS и Firebase

Недавно я отправился в путешествие, чтобы модернизировать свои навыки веб-разработки. Нет ничего плохого в старой доброй установке PHP/Bootstrap/JavaScript, размещенной на любом из последних лучших предложений для виртуального хостинга, но я хочу, чтобы его использование было положительным выбором, а не моим единственным вариантом.



Освоив React, Firebase и TailwindCSS, я разработал Вероятностные эксперименты, простой проект с подбрасыванием монеты, чтобы применить свои теоретические знания и понимание следующих концепций:

  • Управление состоянием в React с помощью хука useState и передачи реквизитов через дерево компонентов
  • Условный рендеринг компонентов на основе обработчиков состояния, например. открытие и закрытие модального элемента.
  • Использование собственного метода JavaScript map() в контексте React
  • Адаптивный дизайн с использованием префиксов в css попутного ветра, например sm:bg-color-red-100
  • Покупка домена через Google Domains и привязка его к хостингу Firebase.

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

Дерево компонентов

В основе React лежат компоненты — многоразовые строительные блоки, состоящие из HTML, CSS и, возможно, JavaScript для логики.

В корне моего сайта находится компонент <App />, который содержит все дочерние компоненты приложения React.

В будущем я могу добавить дополнительные сценарии помимо подбрасывания монеты, но пока <Experiments />содержит только два дочерних компонента: <CoinControls /> и <CoinResults /> .

<CoinControls /> содержит компоненты для общей панели навигации сайта, заголовков, статистики, а также кнопку для подбрасывания монеты. Он также содержит модальное окно, которое скрыто до тех пор, пока пользователь не щелкнет значок информации.

Управление состоянием с помощью хука useState и передача реквизитов через дерево компонентов

Существуют более продвинутые способы управления состоянием при использовании React, например, с помощью useReducer, useContext и даже через целые библиотеки, такие как Redux, однако я хотел изучить основную идею сохранения и передачи состояния между компонентами с помощью хука useState.

Например, при нажатии флип-кнопки useState работает с реквизитами, передаваемыми между различными компонентами, чтобы монеты отображались в разделе результатов, а статистика обновлялась.

Компонент <Experiment /> содержит массив coins, который отслеживает головы и решки, а также функции для обновления и сброса массива:

// INSIDE <Experiment />

const [coins, setCoins] = useState([]);

const addCoinHandler = (coin) => {
    setCoins((prevCoins) => {
      return [coin, ...prevCoins];
    });
  };

const clearCoinsHandler = () => {
    setCoins([]);
  };

Функции обработчика передаются через свойства в <CoinControls />component, и аналогично массив монет передается в <CoinResults />.

<CoinControls onAddCoin={addCoinHandler} onReset={clearCoinsHandler} />
<CoinResults coins={coins} /> 

Доступ к функциям обработчика осуществляется через свойства внутри компонента <CoinControls />; props.onAddCoin и props.onReset становятся указателями на функции, которые фактически определены в <Experiment />, родительском компоненте.

Внутри <CoinControls /> , flipCoin и reset определены функции, которые используют обработчики, переданные через реквизиты из <Experiment />. Эти новые функции, в свою очередь, передаются дальше по дереву компонентов через свойства: <CoinFlip flip = {flipCoin} reset={reset}/>.

// INSIDE <CoinControls /> 

const flipCoin = () => {
    const coin = Math.floor(Math.random() * 2);
    if (coin === 0) {
      setHeads(heads + 1);
    } else {
      setTails(tails + 1);
    }
    props.onAddCoin(coin);
};

const reset = () => {
    setHeads(0);
    setTails(0);
    setPHeads((0).toFixed(5));
    setPTails((0).toFixed(5));
    props.onReset();
};

Таким образом, щелчок кнопки в компоненте <CoinFlip /> вызывает функцию flipCoin, определенную в ее родительском компоненте <CoinControls />, которая, в свою очередь, передает новую монету орлом или решкой дальше вверх по дереву компонентов, чтобы в конечном итоге получить массив монет в <Experiment />.

Условный рендеринг компонентов на основе обработчиков состояния

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

Условный рендеринг компонентов в React довольно прост. Пример его использования показан ниже: когда нажата информационная кнопка рядом с заголовком, в дерево компонентов добавляется модальное окно, содержащее сведения о вероятности подбрасывания монеты, а затем удаляется, когда пользователь нажимает кнопку «ОК» или серое окружение. область.

TailwindCSS упростил стилизацию модального компонента, используя className вместо обычного свойства class. Контент устанавливается динамически с помощью реквизитов, что позволяет легко переназначить компонент, если мне удастся добавить дополнительные сценарии вероятности.

// INSIDE <Modal />
const Modal = (props) => {
  
   const backdrop = "fixed top-0 left-0 w-full h-full bg-black bg-opacity-80 z-1";
  
   const modal = "p-6 fixed w-5/6 lg:w-2/4 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white rounded-lg shadow-lg z-100";
  
   const close = "py-2 px-8 text-center text-2xl border-4 cursor-pointer border-gray-600 bg-gray-100 rounded-lg w-24 h-12 flex justify-center items-center";
  
   return (
    <div>
      <div className={backdrop} onClick={props.info}></div>
      <div className={modal}>
        <h1 className="text-4xl border-b-2">{props.title}</h1>
        <p className="pt-4">
          {props.description}
        </p>
        <p className="pt-4 text-center text-2xl">{props.formula}</p>
        <p className="pt-4 text-center text-2xl">{props.formula2}</p>
        <div className="flex justify-center p-4">
            <div className={close} onClick={props.info}>OK</div>
        </div>
      </div>
    </div>
  );
};

Щелчок значка информации, серого фона или кнопки «ОК» запускает функцию infoHandler, находящуюся в компоненте <CoinControls />, и переключает состояние, если константа info. Это логическое значение находится слева от логического оператора JavaScript, &&, поэтому модальный компонент появляется всякий раз, когда info находится в истинном состоянии.

// INSIDE <CoinControls /> 
const [info, setInfo] = useState(false);

  //Funtion to display modal
  const infoHandler = () => {
    setInfo(!info);
  };

// THEN SOMEWHAT LATER...

{info && (
        <Modal
          info={infoHandler}
          title={modalTitle}
          description={modalDescription}
          formula={modalFormula}
          formula2={modalFormula2}
        />
      )
}

Использование собственного метода map() в JavaScript в контексте React

Массив монет представляет собой просто список из 1 и 0, который добавляется каждый раз, когда нажимается флип-кнопка. Как описано выше, список передается в <CoinResults /> через реквизит.

Используя метод массива map(), монеты появляются на панели результатов, и к ним условно применяются стили в зависимости от того, является ли элемент 1 или 0; с помощью тернарного оператора condition ? IfTrue : IfFalse решка (0) окрашивается в красный цвет, а решка (1)

//INSIDE <CoinResults />

const CoinResults = (props) => {
    const h = "h-16 bg-red-100 border-2 border-red-600 rounded-full w-16 flex items-center justify-center text-4xl";
    const t = "h-16 bg-amber-100 border-2 border-amber-600 rounded-full w-16 flex items-center justify-center text-4xl";

//Elsewhere ...

    {props.coins.map((coins, index) => (
          <div key={index} className={coins===0?h:t} >
              <div className={coins===0?" flex select-none text-red-300":"flex text-amber-300"}>
                  {coins === 0 ? "H" : "T"}
              </div>
           </div>
       ))};

Классная утилита TailwindCSS позволяет легко назначать кучу классов. В некоторых местах я записывал их прямо в выражениях JSX, но когда они становились довольно длинными, я сохранял их в отдельной константе, чтобы облегчить чтение в целом.

Адаптивный дизайн с использованием префиксов в TailwindCSS

В своих последних проектах я использовал Bootstrap в качестве предпочитаемого CSS-фреймворка, и, несмотря на то, что это делает стилизацию очень простой, отрицательный эффект заключается в том, что мои сайты мгновенно узнаются как основанные на Bootsrap. С переходом на TailwindCSS мне стало намного легче контролировать макет.

Ключевой целью этого проекта было использование префиксов с классами для облегчения адаптивного дизайна. Это оказалось очень легко реализовать.

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

<!-- INSIDE <Experiments /> -->

<div className="grid md:grid-cols-3">
        <CoinControls ... />
        <CoinResults  .../> 
</div>

<!-- INSIDE <CoinControls /> -->
<div className="md:col-span-1 ...">
   ...
</div> 

<!-- INSIDE <CoinResults /> (props ommitted) -->
<div className="md:col-span-2 ...">
   ...
</div>

Тот же подход с добавлением префикса работает с любым стилем.

Покупка домена через Google Domains и привязка его к хостингу Firebase

Конечная цель этого проекта заключалась в том, чтобы указать пользовательский домен на проект, размещенный на Firebase. Это также оказалось очень прямолинейным. В панели хостинга Firebase я просто смог следовать подсказке Добавить личный домен. Оттуда я смог купить домен и указать его на свой проект, бесплатный SSL и все такое. Если это то, чего вы хотите достичь, то не ищите ничего, кроме Руководства Google.

Последние мысли

Создание этого проекта было хорошим учебным опытом, и я надеюсь встроить в него больше вероятностных экспериментов в будущем. Кроме того, многократное подбрасывание монеты и наблюдение за результатами было более захватывающим, чем я думал. Я делал это так много раз, что начал задаваться вопросом, действительно ли случайность была случайной, поскольку часто результаты довольно далеко отклонялись от ожидаемых 0,5.

Полный код здесь.

Мне нравится писать о моих проектах, обучении и открытиях. Если вам понравилась эта статья, рассмотрите возможность подписаться на меня и проверить другие мои сообщения.