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

4. Уроки Node.js. Структура Пакета NPM

Продолжаем наш разговор об NPM.Для того чтобы посмотреть на реальный package.json поставим модуль express. Введем в консоли:

npm i [email protected]

Этим модулем мы будем еще пользоваться в будущем, а сейчас мы просто ставим этот внешний модуль и посмотрим на его package.json, как он выглядит. Обнаружим там массу всего интересного.

Мы кратко пройдемся по некоторым особенностям package.json пока что в общих чертах, в дальнейшем, когда мы будем делать конкретные вещи, то используем это для решения конкретных задач. Итак, поля.

Оценка задачи

В первую очередь хочу отметить, что все описанное ниже основано на реальном опыте работы и не является моими личными пожеланиями.

Чтобы правильно оценить задачу, проект нужно:

19. Уроки Node.js. Безопасный Путь к Файлу в fs и path.

В этой статье мы рассмотрим, как при помощи Node.js создать веб-сервер, который будет возвращать файл пользователю из директории public. Может возникнуть вопрос: зачем здесь Node.js? почему бы не сделать это на другом сервере? Вопрос совершенно уместен. Да, для отдачи файлов, как правило, другие сервера будут более эффективны. С другой стороны, Node.js, во-первых, тоже работает весьма неплохо, а во-вторых, перед отдачей файла может совершить какие-то интеллектуальные действия, например, обратиться к базе данных, проверить, имеет ли пользователь право на доступ к данному файлу, и только если имеет, тогда уже отдавать.

Leave a Reply