Теперь начнем разбираться в том как, мы работаем с данными. Пока, для отображения статей мы использовали денормализированную структуру. Ее видно в файле fixtures.js. В каждой статье есть вся информация о ней, она напоминает древовидную структуру. Подобная структура удобна для чтения, но она превратит вашу жизнь в ад если вы начнете как-то изменять эти данные. Если у вас будет более или менее сложная структура, где эти зависимости будут пересекаться, например, у статьи есть автор у комментария есть автор, у автора есть своя страница. Если хранить это в том виде как это есть сейчас, то когда вы захотите поменять имя этого автора, вам придется просмотреть все места где даже чисто теоретически он может использоваться и заменить эти данные, и скорее всего Вы что-то пропустите. Поэтому, перед тем как сохранять данные в stores их нормализируют. Нужно отдельно хранить сущности комментариев, статей, авторов и.т.п, и отдельно хранить связи (например у статьи есть много комментариев), в виде id.
На клиенте удобно хранить данные в одном месте, к примеру, там где много комментариев, там сразу можно хранить массив с id этих комментариев. Это довольно удобно, так как все манипуляции с нашими данными можно будет делать в одномreducer. Давайте реализуем такую структуру. Сделаем reducer для комментариев, для этого в папке reducer создадим файл comments.js:
import { } from '../constants'
export default (comments = {}, action) => {
const { type, payload, response, error } = action
switch (type) {
}
return comments
}Также мы немного изменим то, как мы храним данные, сейчас мы храним их в виде обычного массива, и это не очень удобно, если мы хотим работать с данными. Нам часто нужно обращаться к конкретному элементу, поэтому будет удобнее хранить данные в виде объектов, ключами в которых выступают id (статьи например), а их значением будет сама статья. Таким образом мы значительно упростим обращение к элементам нашего store. Давайте все это перестроим и начнем с reducer/articles.js. В articles мы будем уже брать normalizedArticles, изменим import следующим образом:
import { normalizedArticles } from '../fixtures'
Также в reducer/comments.js добавим import:
import { normalizedComments } from '../fixtures'
Продолжим наш урок с использованием библиотеки immutable.js. Установим ее:
npm i immutable --s
У immutable.js есть разные структуры данных, одна из самых часто используемых List, это аналог массива, просто immutable. Map это аналог объекта.OrderedMap– это вариация Mapгде вы сохраняете последовательность в которой были добавлены элементы. И еще есть такой элемент Record, в который позволяет описать вид в котором у Вас находится элемент. Давайте используем Record и OrderedMap в нашем проекте. Сделаем следующие записи в reducer/articles.js:
import { normalizedArticles } from '../fixtures'
import { DELETE_ARTICLE } from '../constants'
import { OrderedMap, Record } from 'immutable'
const Article = Record({
"id": "",
"date": "",
"title": "",
"text": "",
"comments": []
})
const defaultArticles = normalizedArticles.reduce((acc, el) => {
return acc.set(el.id, new Article(el))
}, new OrderedMap({}))
export default (articles = defaultArticles, action) => {
const { type, payload } = action
switch (type) {
case DELETE_ARTICLE:
return articles.filter(article => article.id != payload.id)
}
return articles
}В переменной defaultArticles мы с Вами идем по массиву normalizedArticles и с помощью метода reduce проходим от первого до последнего элемента массива, собирая их в какую-то другую структуру, в нашем случает в immutabele объект acc, также здесь есть el – это элемент к которому мы сейчас обращаемся (первая статья, вторая статья и т.д.). И начальное значение acc, то с которого мы начинаем – new OrderedMap({})). Возвращаем мы с Вами:
acc.set(el.id, new Article(el))
В константе const Article вы описываете то, какого вида у Вас статьи. В нашем случае мы указываем default значения для статей. Все записи будут храниться в таком виде. В данном случае можно было воспользоваться и обычными объектами и методом mapи тогда нечего особенно для Вас не поменяется. Но Record придуман именно для подобных записей. У Вас появляется гарантированная структура Ваших данных (статей) и она будет всегда выдержана . Также будет удобнее обращаться к immutabele объекту article, к примеру при использовании простого объекта будет выглядеть так:
article.get('title'), если мы используем Record, у нас уже есть готовый getter и мы сможем просто обращаться:article.title.
Далее нужно будет немного поменять наш API, перейдем в containers/Filters.js и сделаем следующие изменения:
export default connect(state => {
const { articles, filters } = state
return {
articles: articles.valueSeq(),
filters
}
}, { changeFiltersРаньше мы рассчитывали что будем работать с массивом, а сейчас мы получаем объект. Поэтому мы преобразуем его в connect. Для того чтобы превратить структуру OrderedMapв массив List мы вызываем метод valueSeq() таким образом у нас получится просто массив статей.
Также у нас могут быть проблемы в select, т.к. мы ожидали что в нем у нас приходит массив, а теперь мы получаем custom structure. Чтобы ошибок не произошло необходимо пойти в атрибуту options превратив их в toJS():
<Select
options = {options.toJS()}
multi = {true}
value = {filters.selectedArticles}
onChange = {this.handleSelectChange}
/>Также необходимо будет исправить containers/Articles.js:
function filterArticles(articles, { from, to, selectedArticles }) {
return articles.valueSeq()Почему мы вызываем valueSeq()? Потому, что articles – это объект (ключ : значение), а мы хотим достать из него значения и поместить их в массив. Аналогично этому есть также keySeq , для того чтобы достать ключи. Дальше преобразовывать ничего не надо, так как React прекрасно понимает immutable.js, и его структуры. Поэтому не надо переводить их в нативные JS структуры.
Код урока вы можете найти в нашем репозитории.
We are looking forward to meeting you on our website blog.soshace.com


