简体   繁体   中英

Structuring the store of a localized react / redux app

I am having a hard time structure my data in a localized blogging app.

My app is displaying posts, with embedded pictures (one-to-many), in three languages (English, French and Russian).

The visitor can choose its locale. The editors can edit the localized versions of their posts in the three languages.

For the moment, the structure of my store looks like this:

{ articles:
  {
    en: {
      1: { id: 1, title: "my first title", body: "my first body", picture_ids: [1, 2, 3]},
      2: { id: 2, title: "my second title", body: "my second body", picture_ids: [1, 4, 5]},
      3: { id: 3, title: "my third title", body: "my third body", picture_ids: [6, 7, 8]},
      ...
    },
    fr: {
      1: { id: 1, title: "mon premier titre", body: "mon premier corps de texte", picture_ids: [1, 2, 3]},
      2: { id: 2, title: "mon second titre", body: "mon second corps de texte", picture_ids: [1, 4, 5]},
      3: { id: 3, title: "mon troisième titre", body: "mon troisième corps de texte", picture_ids: [6, 7, 8]},
      ...
    }
  },
  pictures:
   {
      en: {
        1: { id: 1, title: "My great picture", url: "http://..."},
        2: { id: 2, title: "My other great picture", url: "http://..."},
        3: { id: 3, title: "My not so great picture", url: "http://..."},
        ...
      },
      fr: {
        1: { id: 1, title: "Ma superbe photo", url: "http://..."},
        2: { id: 2, title: "Mon autre superbe photo", url: "http://..."},
        3: { id: 3, title: "Ma photo pas vraiment superbe", url: "http://..."},
        ...
      }
   },
   editStateOfFieldsOfArticles:
   {
      en: {
        1: { title: true, body: false },
        2: { title: false, body: true },
        3: { title: false, body: false },
        ...
      },
      fr: {
        1: { title: false, body: false },
        2: { title: false, body: false },
        3: { title: true, body: true },
        ...
      }
   }
}

At this stage, my reducers are not too bloated but I have the feeling that I should normalize further in order to anticipate when I am going to add tags, authors and other internationalized items in relations with the articles.

So I am considering (i) creating an additional dictionary in the store for the languages, (ii) "flatten" all the other dictionaries to get rid of the "locale" substore keys, (iii) add a language_id field in each of the items stored in the other dictionaries and (iv) modify the numeric keys in each of my dictionaries as composite keys. This would look like this:

{languages:
  {
    1: {id: 1, locale: "en", long: "English"},
    2: {id: 2, locale: "fr", long: "Français"},
    3: {id: 3, locale: "ru", long: "русская"}
  }
  articles:
  {
    11: {id: 1, title: "my first title", body: "my first body", picture_ids: [1, 2, 3], language_id: 1},
    21: {id: 2, title: "my second title", body: "my second body", picture_ids: [1, 4, 5], language_id: 1},
    31: {id: 3, title: "my third title", body: "my third body", picture_ids: [6, 7, 8], language_id: 1},
    42: {id: 1, title: "mon premier titre", body: "mon premier corps de texte", picture_ids: [1, 2, 3], language_id: 2},
    52: {id: 2, title: "mon second titre", body: "mon second corps de texte", picture_ids: [1, 4, 5], language_id: 2},
    62: {id: 3, title: "mon troisième titre", body: "mon troisième corps de texte", picture_ids: [6, 7, 8], language_id: 2},
    ...
  },
  pictures:
  {
    11: {id: 1, title: "My great picture", url: "http://...", language_id: 1},
    21: {id: 2, title: "My other great picture", url: "http://...", language_id: 1},
    31: {id: 3, title: "My not so great picture", url: "http://...", language_id: 1},
    12: {id: 1, title: "Ma superbe photo", url: "http://...", language_id: 2},
    22: {id: 2, title: "Mon autre superbe photo", url: "http://...", language_id: 2},
    32: {id: 3, title: "Ma photo pas vraiment superbe", url: "http://...", language_id: 2},
    ...
  },
  editStateOfFieldsOfArticles:
  }
    11: {title: true, body: false, language_id: 1},
    21: {title: false, body: true, language_id: 1},
    31: {title: false, body: false, language_id: 1},
    12: {title: false, body: false, language_id: 2},
    22: {title: false, body: false, language_id: 2},
    32: {title: true, body: true, language_id: 2},
    ...
  }
}

The last digit of the keys of the "articles", "pictures" and "editStatesOfFieldsOfArticles" dictionaries would correspond to the locale/language_id and the other digits would correspond to the item id.

I see that I would benefit from such a flatter structure by getting rid of the need to iterate over the languages when I have a store modification that applies to all three languages (for instance, if I delete an article, the article should deleted in all three languages and I currently have to do a for...in on the localized sub-dictionaries and all the ancillary store's sub-dictionaries (such as "editStateOfFieldsOfArticles" (I have a handful of other such ancillary dictionaries)).

However, I am not completely sure that I would really take any other benefits from flattening further my hash (and the for...in loops are not that problematic with only three languages to manage -- in addition, in my use case where I need to delete an article and this deletion needs to be reflected in all three languages, I would still have to iterate over the three languages array to delete the three corresponding records in the flattened articles' dictionary).

Also, I am concerned with performance issues: since I will be storing my whole collections (of articles or any other internationalized items) under the same flat trees, whatever the language they relate to, I fear that accesses to the values might slow down compared to a more structured tree where I can access items by "sub-keying" down the localized branches -- and as a matter of fact, the backend database stores the localized articles in distinct localized tables).

I would be really grateful to anyone with experience in structuring Redux stores for internationalized content or other stores with complex cross-relationships who could provide some feedback or advices or tips on its own experience regarding: - code readability, in particular in the reducers and in the memoized selectors, - compared performances of nested trees vs flat trees, - the overall benefits of further normalizing vs. keeping a "bit" of nesting.

Super late to this party, but in case you're still curious.

I think your first implementation is definitely superior. A for loop over 3 items is not a taxing transaction, and you can (and should) factor that out into a helper function:

var forEachLanguage = function(state, dataType, callback){
    var languages_data = state[dataType];
    for(language_index in languages_data){
        var data = languages_data[language_index];
        callback(data);
    }
}

//Example for deleting item 2 from articles
forEachLanguage(state, 'articles', function(data){
    delete data[2];
});

Furthermore, if you're concerned about repeated lookups getting needlessly expensive (though realistically, I don't see this being a problem), you can use Reselect to cache your lookups and make re-usable lookup functions that incorporate locale.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM