На предыдущем уроке мы научились более удобным способом писать reducers
используя удобные API
для добавления/удаления элементов, не беспокоясь о том, что мы что-то изменим по дороге.
Теперь если мы с Вами посмотрим на наше приложение и откроем какую-нибудь статью, то увидим в console
warnning
. Наши propTypes
предупреждают нас о наличии проблемы, еще до того момента как мы до нее доберемся. Это огромный плюс – то что мы их написали. О чем говорит этот warnning
? Он говорит о том что мы ожидали получить в Comment
объект с комментарием, а сейчас передаем туда число. И действительно если открыть комментарии, то их там не будет. Для того чтобы они появились нам нужно пойти в часть store
которая отвечает за comments
и достать оттуда необходимые комментарии. Аналогично тому, что мы сделали с Вами со статьями, нам предстоит сделать с комментариями.
Изменим reducer/comments.js
следующим образом:
import { } from '../constants' import { normalizedComments } from '../fixtures' import { Record } from 'immutable' import { recordsFromArray } from './utils' const Comment = Record({ "id": null, "user": "", "text": "" }) const defaultComments = recordsFromArray(Comment, normalizedComments) export default (comments = defaultComments, action) => { const { type, payload, response, error } = action switch (type) { } return comments }
А также создадим файл reducer/utils.js
и вынесем туда часть нашей логики, которую будет удобно переиспользовать в дальнейшем, а не просто ее копировать:
import { OrderedMap } from 'immutable' export function recordsFromArray(RecordType, array) { return array.reduce((acc, el) => { return acc.set(el.id, new RecordType(el)) }, new OrderedMap({})) }
Здесь наши с помощью recordsFromArray
данные превращаются в immutable.js структуру.
Также изменить reducer/articles.js
:
import { normalizedArticles } from '../fixtures' import { DELETE_ARTICLE } from '../constants' import { Record } from 'immutable' import { recordsFromArray } from './utils' const Article = Record({ "id": "", "date": "", "title": "", "text": "", "comments": [] }) const defaultArticles = recordsFromArray(Article, normalizedArticles) export default (articles = defaultArticles, action) => { const { type, payload } = action switch (type) { case DELETE_ARTICLE: return articles.filter(article => article.id != payload.id) } return articles }
И последний шаг изменим import
в reducer/index.js
:
import articles from './articles' import comments from './comments' import counter from './counter' import filters from './filters' import { combineReducers } from 'redux' export default combineReducers({ count: counter, articles, filters, comments })
Теперь нам нужно эти комментарии как-то достать, раньше они были внутри статей, а теперь там есть только ссылки. Чтобы это сделать мы пойдем в то место, где мы их использовали, а именно в components/Article/index.js
. Мы должны решить, будем мы получать их в commentList
или же в самом компоненте статьи. Забегая вперед сейчас мы отдадим предпочтение commentList
. Нам сейчас нужно обернуть наш компонент в connect
и достать по id
комментариев сами комментарии. Мы можем это сделать в компоненте Article/index.js
, здесь у нас уже есть connect
и мы его можем использовать, чтобы достать comments
из store
. А можем это сделать в commentList
уже здесь получив
id
просто достав их из store
. Зайдем в commentList
и сделаем import
:
import { connect } from 'react-redux'
Далее в самом низу изменим export default
:
export default connect((state, { comments }) => { return { commentObjects: comments.map(id => state.comments.get(id)) } })(toggleOpen(CommentList))
Помимо аргумента state
у Вашего store
есть второй props
, которые и так приходили в Ваш компонент. А приходит в props
у нас comments
. Давайте это используем. Чтобы не путаться назовем то что у нас будет в return
– commentObjects
, подчеркивая, что это уже объект. Здесь мы пройдемся по массиву комментариев и с помощью immutable map
и по id
будем доставать из state
наши комментарии (т.е. records комментариев).
В render
мы уже работаем с нашим объектом, поэтому код станет выглядеть так:
render() { const { commentObjects, isOpen, toggleOpen } = this.props
Также в reducer/articles.js
изменим фильтрацию, так как нам она больше не нужна в том виде в котором она была написана.
return articles.delete(payload.id)
Мы просто описываем какую статью нужно удалить из объекта. Если нужно добавить вы используете set()
если обновить то update()
.
articles.set() articles.update()
Это пригодиться в будущем домашнем задании. Также Immutable.js
прекрасно работает с глубокой вложенностью. Если вдруг Вам нужно обновить комментарии, которые вложены в статью которые, вложены в объект со всеми статьями, который возможно вложен в еще какой-нибудь объект, который помимо всего хранит другие всевозможные данные, то вы замучаетесь писать функции-обертки. Для таких случаев в Immutable.js
есть:
articles.updateIn([id, 'comments'], comments => ...)
Проверяем, все прекрасно работает!
Мы с Вами уже нормализировали данные и храним отдельно статьи и комментарии, это пойдет на пользу когда мы будем читать данные с API
так как очень часто REST API
отдают данные в таком виде. Т.е. end points
отвечают за какие-нибудь ресурсы, например за статью или за комментарий и далеко не всегда у вас есть API
который сразу готов отдать древовидную структуру статьи со всеми комментариями и.т.д. Такую проблему решает GraphQL например. Relay и GraphQL это React структура – то что Facebook использует у себя для fetching’а данных, но это уже довольно сложные темы. Но обычно наш API
будет в таком виде:
http://localhost:8080/api/article
Отдельно будет API для комментариев:
http://localhost:8080/api/comment
И нужно будет заставить их работать вместе (“подружить” их). Чем мы в дальнейшем и займемся, получая все статьи, комментарии из реального API
.
Далее мы продолжим знакомиться с Redux API и посмотрим что же такое Middleware. Все что мы до этого момента делали прекрасно описывалось чистыми функциями и в принципе в Redux все стоит делать чистыми функциями, это и action creators
и reducers
. Они получают на входе старые данные и action
и возвращают на выходе новый объект с новыми статьями, не меняя ничего во “внешнем мире”.
С другой стороны в реальной жизни не все можно описать чистыми функциями, иногда нам нужны side-effects
– это любое обращение к API, Logging, Reporting и.т.д. Все это вынесено в middleware
. Это то что находиться между тем как вы произвели dispatch
над action
и тем как это попало в Ваши reducers
.
We are looking forward to meeting you on our website soshace.com
Здравствуйте! А нельзя ли ссылку на весь файл CommentList.js, ибо с правками, которые вы даете комментариев не видно. Как вот здесь передать text:
const listComments = commentObjects.map((comment) => {
return(
)
});
Добрый день! Вот ссылка на файл в репозитории:
https://github.com/soshace/react_lessons/blob/lesson_11/src/components/CommentList.js
Прочтите вторую часть 11 урока обязательно, тогда должно стать все понятно!
Спасибо, уже нашел проблему. Сам натупил: сделал опечатку))) Зато глубже разобрался в механизмах immutable и логике приложения. Ваши уроки, как по мне, довольно сложные, но как говорил Омар Хаям: “Никогда не сдавайся! Если тебе тяжело — значит ты на верном пути!