31Oct

react_11_1

На предыдущем уроке мы научились более удобным способом писать 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

 

15. Уроки Node.js. Асинхронная разработка. Введение.

В реальной жизни очень редко бывает так, что, получив запрос, сервер может тут же на него ответить. Обычно для того, чтобы ответить, серверу нужны какие-то данные. Эти данные он получает либо из базы, либо из какого-то другого источника, например, из файловой системы. В этом примере, используя модуль fs при получении запроса на url ‘/’, считывается файл index.html и выводится посетителю.

3 Replies to “Уроки React. Урок 11. Pt.1.”

  1. Здравствуйте! А нельзя ли ссылку на весь файл CommentList.js, ибо с правками, которые вы даете комментариев не видно. Как вот здесь передать text:

    const listComments = commentObjects.map((comment) => {
    return(

    )
    });

    1. Добрый день! Вот ссылка на файл в репозитории:

      https://github.com/soshace/react_lessons/blob/lesson_11/src/components/CommentList.js

      Прочтите вторую часть 11 урока обязательно, тогда должно стать все понятно!

      1. Спасибо, уже нашел проблему. Сам натупил: сделал опечатку))) Зато глубже разобрался в механизмах immutable и логике приложения. Ваши уроки, как по мне, довольно сложные, но как говорил Омар Хаям: “Никогда не сдавайся! Если тебе тяжело — значит ты на верном пути!

Leave a Reply