Как обрабатывать вложенные ответы API в приложении Flux?

Я переношу существующее приложение во Flux, и меня немного смущает одна тема. Скажем, у меня есть несколько конечных точек API, которые возвращают двух- или трехуровневые вложенные объекты.

Например, GET /articles может вернуть ответ JSON схемы

articles: article*

article: {
  author: user,
  likers: user*
  primary_collection: collection?
  collections: collection*
}

collection: {
  curator: user
}

Как видите, есть все виды пользователей на разных уровнях вложенности:

  • articles[i].author
  • articles[i].likers[i]
  • articles[i].primaryCollection.curator
  • articles[i].collections[i].curator

Если я хочу обновлять UserStore свежими данными всякий раз, когда извлекаются статьи, мне придется написать чудовищный метод, который проверяет все вложенные сущности в ответе API статьи. Более того, будет много дублирования, потому что есть и другие конечные точки API с другими схемами, а иногда статьи встраиваются внутрь пользователей (например, GET /user/published).

Есть ли более чистый способ для хранилищ Flux извлекать вложенные сущности из всех ответов API?


person Dan Abramov    schedule 20.08.2014    source источник


Ответы (1)


Подход, предложенный Цзинем Ченом (одним из создателей и пропагандистов Flux), заключался в том, чтобы сгладить ответы API до того, как они попадут в Магазины. Я написал небольшую библиотеку, которая делает именно это: нормализует

[{
  id: 1,
  title: 'Some Article',
  author: {
    id: 1,
    name: 'Dan'
  }
}, {
  id: 2,
  title: 'Other Article',
  author: {
    id: 1,
    name: 'Dan'
  }
}]

to

{
  result: [1, 2],
  entities: {
    articles: {
      1: {
        id: 1,
        title: 'Some Article',
        author: 1
      },
      2: {
        id: 2,
        title: 'Other Article',
        author: 1
      }
    },
    users: {
      1: {
        id: 1,
        name: 'Dan'
      }
    }
  }
}

(Обратите внимание, что дублирования нет, а структура плоская.)

Normalizr позволяет:

  • Вложение сущностей в другие сущности, объекты и массивы
  • Комбинируйте схемы сущностей, чтобы выразить любой ответ API
  • Автоматически объединять объекты с одинаковыми идентификаторами (с предупреждением, если они различаются)
  • Используйте собственный атрибут идентификатора (например, slug)

Чтобы использовать его, вам нужно определить свои сущности и правила вложенности и использовать их для преобразования JSON:

var normalizr = require('normalizr'),
    normalize = normalizr.normalize,
    Schema = normalizr.Schema,
    arrayOf = normalizr.arrayOf;

// First, define a schema:

var article = new Schema('articles'),
    user = new Schema('users'),
    collection = new Schema('collections');

// Define nesting rules:

article.define({
  author: user,
  collections: arrayOf(collection)
});

collection.define({
  curator: user
});


// Usage:

// Normalize articles
var articlesJSON = getArticleArray(),
    normalized = normalize(articlesJSON, arrayOf(article));

// Normalize users
var usersJSON = getUsersArray(),
    normalized = normalize(usersJSON, arrayOf(user));

// Normalize single article
var articleJSON = getArticle(),
    normalized = normalize(articleJSON, article);

Это позволяет вам нормализовать любой ответ XHR перед его передачей в Flux Dispatcher. Магазинам нужно будет только обновить себя из соответствующего словаря:

// UserStore

UserStore.dispatchToken = AppDispatcher.register(function (payload) {
  var action = payload.action;

  switch (action.type) {
  // you can add any normalized API here since that contains users:
  case ActionTypes.RECEIVE_ARTICLES:
  case ActionTypes.RECEIVE_USERS:

    // Users will always be gathered in action.entities.users
    mergeInto(_users, action.entities.users);
    UserStore.emitChange();
    break;
  }
});


// ArticleStore

AppDispatcher.register(function (payload) {
  var action = payload.action;

  switch (action.type) {
  // you can add any normalized API here since that contains articles:
  case ActionTypes.RECEIVE_ARTICLES:

    // Wait for UserStore to digest users
    AppDispatcher.waitFor([UserStore.dispatchToken]);

    // Articles will always be gathered in action.entities.articles
    mergeInto(_articles, action.entities.articles);
    ArticleStore.emitChange();
    break;
  }
});
person Dan Abramov    schedule 20.08.2014
comment
Как нормализация обрабатывает данные, которые необходимо передать обратно на сервер, например, для UPDATE/POST? - person HarryH; 15.06.2015
comment
Вы также можете денормировать с помощью normalizr. - person René Stalder; 07.03.2017