Веб-компонент предотвращает множественные выборки API

Я работаю над небольшим веб-компонентом, который конвертирует сумму денег в другую валюту. Для этого я использую API, который возвращает курсы обмена.

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

Это отлично работает, но мне интересно, является ли это плохой практикой или это даже не нужно, потому что, возможно, оно кешируется и т. Д.

Код (описываемая часть находится в функции connectedCallback):

const template = document.createElement('template');
template.innerHTML = `
  <style>
    .curr-shell {
      color: #262626;
      background: #F1F5F9;
      border-radius: 16px;
      padding: 4px 8px;
      font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
      box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
    }
  </style>
  <span class="curr-shell"></span>`;

class CurrencyConverter extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }

  async fetchApi() {
    return new Promise((resolve, reject) => {
      fetch(`https://api.exchangeratesapi.io/latest?base=${this.baseCurrency}`)
        .then((response) => response.json())
        .then((data) => resolve(data))
        .catch((err) => reject(err));
    });
  }

  renderData() {
    const browserLocale = navigator.language;
    const originFormat = new Intl.NumberFormat(browserLocale, {
      style: 'currency',
      currency: this.baseCurrency,
    });
    const conversionFormat = new Intl.NumberFormat(browserLocale, {
      style: 'currency',
      currency: this.conversionCurrency,
    });

    const originAmount = originFormat.format(this.value);

    this.convertedAmount =
      this.value *
      window.rates[this.baseCurrency].data.rates[this.conversionCurrency];

    const conversionAmount = conversionFormat.format(this.convertedAmount);

    this.shadowRoot.querySelector(
      '.curr-shell'
    ).innerText = `${originAmount} | ${conversionAmount}`;
  }

  async connectedCallback() {
    this.baseCurrency = this.getAttribute('base-currency');
    this.value = parseFloat(this.getAttribute('value'));
    this.conversionCurrency = this.getAttribute('conversion-currency');

    if (!window.rates) window.rates = {};

    if (!window.rates[this.baseCurrency]) {
      // first one -> fetch api
      window.rates[this.baseCurrency] = {
        status: 'fetching',
        data: null,
        callbacks: [],
      };

      window.rates[this.baseCurrency].data = await this.fetchApi();
      window.rates[this.baseCurrency].status="loaded";
      this.renderData();
      // resolve all callbacks from the waiting ones
      window.rates[this.baseCurrency].callbacks.forEach((cb) => cb());
    } else if (window.rates[this.baseCurrency].status === 'fetching') {
      // currently some else is fetching -> add callback to be called when done
      window.rates[this.baseCurrency].callbacks.push(() => this.renderData());
    } else {
      // all data loaded
      this.renderData();
    }
  }
}

window.customElements.define('currency-converter', CurrencyConverter);

Демо: https://phartenfeller.github.io/currency-converter-wc/demo/

1 ответ
1

Из суперкраткого обзора;

  • Я бы прикрепил тег стиля к элементу head, особенно если у вас может быть этот элемент несколько раз на странице

  • Когда все, что делает функция жирной стрелки, — это вызов другой функции с предоставленным параметром (ами), вы также можете

      .then(resolve)
      .catch(reject);
    

    вместо

      .then((data) => resolve(data))
      .catch((err) => reject(err));
    
  • Это не сработает в случае дождливого дня;

     window.rates[this.baseCurrency].data.rates[this.conversionCurrency];
    

    Подумайте, как бы вы справились с этим, если бы window.rates[this.baseCurrency] пропал, отсутствует.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *