Skip to content
This repository has been archived by the owner on May 5, 2020. It is now read-only.

Latest commit

 

History

History

009-data-state

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Состояние загружаемых данных

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

Часто требуется сохранить состояние загружаемых данных — например, списка комментариев к статье. Обычно для этого заводят булевы флаги:

const comments = {
  isLoading: false,
  isLoaded: false,
  isError: false,
  errorText: '',
  data: [],
};

Флаги легко использовать во внешнем коде, но ими сложно управлять. Так может выглядеть код, управляющий их состоянием:

const comments = {
  isLoading: false,
  isLoaded: false,
  isError: false,
  errorText: '',
  data: [],
};

function loadComments() {
  comments.isLoaded = false;
  comments.isError = false;
  comments.isLoading = true;

  fetch('/comments')
    .then((response) => response.json())
    .then((data) => {
      comments.isLoading = false;
      comments.isLoaded = true;
      comments.data = data;
    })
    .catch((error) => {
      commments.isLoading = false;
      comments.isError = true;
      comments.errorText = error.message;
    });
}

Получается запутанно и многословно, нужно помнить, значения каких флагов следует сбросить. Более практичный подход — описывать состояние данных одним полем dataState, а все флаги автоматически вычислять на основе значения этого поля:

const dataStates = {
  notAsked: 'notAsked',
  loading: 'loading',
  loaded: 'loaded',
  failed: 'failed',
};

const comments = {
  dataState: dataStates.notAsked,
  errorText: '',
  data: [],

  get isLoading() {
    return this.dataState === dataStates.loading;
  },

  get isLoaded() {
    return this.dataState === dataStates.loaded;
  },

  get isError() {
    return this.dataState === dataStates.failed;
  }
};

Кода стало чуть больше, зато теперь значения всех флагов меняются автоматически, и вероятность ошибки из-за неправильного обновления одного из флагов сведена к нулю. Функция загрузки комментариев становится значительно проще:

function loadComments() {
  comments.dataState = dataStates.loading;

  fetch('/comments')
    .then((response) => response.json())
    .then((data) => {
      comments.dataState = dataStates.loaded;
      comments.data = data;
    })
    .catch((error) => {
      comments.dataState = dataStates.failed;
      comments.errorText = error.message;
    });
}

Дополнительное преимущество такого подхода — расширяемость. Представим, что для данных нужно добавить ещё одно состояние — например, deprecated. Добавление нового состояния ограничивается лишь правкой объекта comments:

const comments = {
  /* ... */,

  get isDeprecated() {
    return this.dataState === dataStates.deprecated;
  },
};

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