07Oct


Всем привет! Сегодня начнем наш урок с домашнего задания, сделав его вместе. Главной целью этих уроков является  помочь читателю научиться думать категориями данных – это является основой функционального программирования как такового. когда вы любую систему, например UI, вы описываете  набором данных. Пока в наших уроках мы делали все самыми простыми способами. У нас были статьи которые содержат в себе все необходимое, счетчик который содержит в себе число и т.д. Мы пока не делали ничего сложнее, не объединяли все эти элементы, мы не думали какие данные стоит хранить в store а какие в state компонентов. Поэтому давайте все это обсудим. Во-первых, какие данные и где следует держать?

В идеале в store должна храниться вся информация которая достаточна для описания вашего приложения , т.е. у вас в state компонентах ничего не должно быть, но на практике это не совсем так, и нет смысла переносить некоторые элементы из state в store. Например бывают проекты где при работе с формами при каждом нажатии изменения заносятся в state, с нашей точки зрения это лишнее. При работе с довольно стандартным приложением вы должны хранить в store такие данные которых будет достаточно для полного восстановления вашего приложения. Это дает понимание чем можно пожертвовать при написании приложения. Например если пользователь перезагрузит страницу и у него закроется календарь – что наверное не так важно, это можно отнести в state. Здесь лучше хранить то,что не жать потерять. Например, нам важно количество статей, т.е. если удалились пара статей вам важно об этом знать. Вы хотели бы информацию эту информацию – ее место в store. Как раз мы подошли к вопросу о фильтрах и их создании. Давайте сделаем reducer который будет хранить значение фильтров. Сейчас мы все храним в компоненте в ArticleList , а мы их вынесем в отдельный компонент/контейнер.

Создадим Filters.js в папке containers.  И сюда мы вынесем все что нам необходимо для фильтров. Таким образов в ArticleList останется:

import React, { Component }  from 'react'
import Article from './Article/index'
import oneOpen from '../decorators/oneOpen'
import Filters from '../containers/Filters'

class ArticleList extends Component {


    render() {
        const { articles, isItemOpen, toggleOpenItem } = this.props

        const listItems = articles.map((article) => <li key={article.id}>
            <Article article = {article}
                isOpen = {isItemOpen(article.id)}
                openArticle = {toggleOpenItem(article.id)}
            />
        </li>)

        return (
            <div>
                <h1>Article list</h1>
                <Filters />
                <ul>
                    {listItems}
                </ul>
            </div>
        )
    }
}

export default oneOpen(ArticleList)

Где мы добавим import Filters и отобразим их в return.
Все остальное у нас переместилось в компонент Filters.

Добавляем  { conncect } в import и в export default

import React, { Component, PropTypes } from 'react'
import Select from 'react-select'
import 'react-select/dist/react-select.css'
import DayPicker, { DateUtils } from "react-day-picker"
import 'react-day-picker/lib/style.css'
import { connect } from 'react-redux'

class Filters extends Component {
    static propTypes = {

    };

    state = {
        selectedArticles: null,
        from: null,
        to: null
    }

    render() {
        const { articles } = this.props
        const options = articles.map((article) => ({
            label: article.title,
            value: article.id
        }))
        return (
            <div>
                {this.getRangeTitle()}
                <Select
                    options = {options}
                    multi = {true}
                    value = {this.state.selectedArticles}
                    onChange = {this.handleSelectChange}
                />
                <DayPicker
                    ref="daypicker"
                    selectedDays={day => DateUtils.isDayInRange(day, this.state)}
                    onDayClick={this.handleDayClick}
                />

            </div>
        )
    }
    getRangeTitle() {
        const { from, to } = this.state
        const fromText = from && `Start date: ${from.toDateString()}`
        const toText = to && `Finish date: ${to.toDateString()}`

        return <p>{fromText} {toText}</p>
    }

    handleDayClick = (e, day) => {
        const range = DateUtils.addDayToRange(day, this.state);
        this.setState(range)
    }

    handleSelectChange = (selectedArticles) => {
        this.setState({
            selectedArticles: selectedArticles.map(o => o.value)
        })
    }
}

export default connect(state => {
    const { articles } = state
    return { articles }
})(Filters)

Проверяем, все должно работать, хотя ничего особенного мы не сделали пока. Что действительно важно так это то что нам не нужен state. Мы должны сделать так чтобы все эти элементы такие как selectedArticles, from хранились в reducer . Создадим в папке reducer файл filters.js :

import { } from '../constants'

const defaultFilters = {
    selectedArticles: null,
    from: null,
    to: null
}

export default (filters = defaultFilters, action) => {
    const { type, payload, response, error } = action

    switch (type) {
     
    }

    return filters
}

В reducer/index.js подключим :

import articles from './articles'
import counter from './counter'
import filters from './filters'
import { combineReducers } from 'redux'

export default combineReducers({
    count: counter,
    articles, filters
})

И сразу же научим их меняться, для этого зайдем в constants.js и добавим:

export const CHANGE_FILTERS = 'CHANGE_FILTERS'

Также не забудем создать action creater – filters.js:

import { CHANGE_FILTERS } from '../constants'


export function changeFilters(change) {
    return {
        type: CHANGE_FILTERS,
        payload: { change }
    }
}

Теперь доделаем наш reducer таким образом:

import { CHANGE_FILTERS } from '../constants'

const defaultFilters = {
    selectedArticles: [],
    from: null,
    to: null
}

export default (filters = defaultFilters, action) => {
    const { type, payload, response, error } = action

    switch (type) {
        case CHANGE_FILTERS:
            return Object.assign({}, filters, payload.change)
    }

    return filters
}

В return внизу чтобы состояние не мутировали добавим следующую запись. Напомню, что в React мы не меняем прошлое состояние а всегда возвращаем новое. Если Redux видит что ничего не поменялось он просто не вызовет callbacks которые подписаны на ваши изменения.Теперь в containers/filters.js делаем импорт нашего AC:

import { changeFilters } from '../AC/filters'

Также добавим добавим его в connect, а также из state будем брать не только статьи но и саами фильтры:

export default connect(state => {
    const { articles, filters } = state
    return { articles, filters }
}, { changeFilters })(Filters)

Теперь все, что мы читали из state, нам нужно читать из filters:

  render() {
        const { articles, filters } = this.props
        const options = articles.map((article) => ({
            label: article.title,
            value: article.id
        }))
        return (
            <div>
                {this.getRangeTitle()}
                <Select
                    options = {options}
                    multi = {true}
                    value = {filters.selectedArticles}
                    onChange = {this.handleSelectChange}
                />
                <DayPicker
                    ref="daypicker"
                    selectedDays={day => DateUtils.isDayInRange(day, filters)}
                    onDayClick={this.handleDayClick}
                />

            </div>
        )
    }
    getRangeTitle() {
        const { from, to } = this.props.filters
        const fromText = from && `Start date: ${from.toDateString()}`
        const toText = to && `Finish date: ${to.toDateString()}`

        return <p>{fromText} {toText}</p>
    }

    handleDayClick = (e, day) => {
        const { filters, changeFilters } = this.props
        const range = DateUtils.addDayToRange(day, filters);
        changeFilters(range)
    }

    handleSelectChange = (selectedArticles) => {
        this.props.changeFilters({
            selectedArticles: selectedArticles.map(o => o.value)
        })
    }
}

Теперь все данные ходят через Redux store и  полностью хранятся там.
Поговорим немного о фильтрах. Теперь в Articles.js нам доступны filters. Сейчас в store хранятся как статьи так и  фильтры. Мы хотим видеть уже отфильтрованные статьи. Лучшее место где это можно делать так это в connect. Саму функцию фильтрации можно выносить в utils к примеру, можно писать прямо в Articles, это уже на ваш вкус. Напишем пока здесь, хотя в реальной жизни лучше вынести это в отдельный файл:

export default connect(({ articles, filters }) => {
    return {
        articles: filterArticles(articles, filters)
    }
})(Articles)

function filterArticles(articles, { from, to, selectedArticles }) {
    return articles
        .filter((article) => selectedArticles.length ? selectedArticles.includes(article.id) : true)
        .filter(article => (!from || Date.parse(article.date) > from) && (!to || Date.parse(article.date) < to))
}

С самого начала буде хранить пустой массив в reducer/filters.js:

const defaultFilters = {
    selectedArticles: [],
    from: null,
    to: null

Таким образом  мы отдельно храним данные про статьи, отдельно информацию про фильтры. Когда нам нужно мы берем информацию из обеих источников, из фильтров и статей, фильтруем . А в Articles у нас благодаря connect появляются наши данные, готовые, отфильтрованные. Данные в store попадают только через reducer’ы. В store храниться какое-то состояние, когда происходит dispatch какого-то action с помощью storeDispatch, с помощью action creator который обернут у нас при помощи connect. Когда мы делаем dispatch action, он попадает в reducers, они берут старое состояние store и action который мы dispatch (например изменение фильтра), мы берем старые фильтры которые у нас  были и action, из него достаем change и делаем merge старых фильтров  и новых. Только все это делаем в новый объект, чтобы у нас не мутировали старые. После того как reducer закончил свою работу store берет новое состояние, сохраняет его и вызывает все callbacks которые были подписаны с помощью store.subscribe и обновляет соответствующие компоненты.

Код домашнего задания находиться здесь.

react-js

We are looking forward to meeting you on our website soshace.com

20. Уроки Node.js. Потоки данных в Node.JS, fs.ReadStream

Всем привет! Тема этого занятия: Потоки данных в Node.js. Мы постараемся разобраться в этой теме более подробно, поскольку, с одной стороны, так получается, что потоки в обычной браузерной JavaScript разработке отсутствуют, а с другой стороны, уверенное владение потоками необходимо для грамотной серверной разработки, поскольку поток является универсальным способом работы с источниками данных, которые используются повсеместно.

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

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

Уроки React. Урок 12.

Всем привет! Сейчас мы пробежимся по нашему домашнему заданию, а также дадим важные комментарии , которые помогут Вам в дальнейшей работе с React. Для удобства Вам понабиться видеть перед собой код, поэтому перейдите на нужный commit и мы продолжим. Начнем с connect.connect нужен нам, чтобы обратиться к store. Добавим небольшую ремарку, что если Вы можете передать данные используя обычные props – передавайте. Например в containers/Articles.js мы обращаемся к store чтобы из его state достать необходимые нам статьи:

Leave a Reply