我正在将现有的应用程序移植到Flux,并且对一个主题有些困惑。假设我有几个API端点,它们返回两级或三级嵌套对象。
例如,GET /articles可能返回架构的JSON响应
GET /articles
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)。
UserStore
GET /user/published
Flux商店是否有更干净的方法 从所有API响应中提取嵌套实体?
Jing Chen(Flux的创建者和传播者之一)建议的一种方法是在API响应到达商店之前对其进行展平。我写了一个小库,它就是这样做的:
[{ id: 1, title: 'Some Article', author: { id: 1, name: 'Dan' } }, { id: 2, title: 'Other Article', author: { id: 1, name: 'Dan' } }]
至
{ 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 可让您:
要使用它,您需要定义您的实体和嵌套规则,并使用它们来转换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; } });