Основы Redux (теория)

Курс рассчитан на создание приложения по шагам, а это значит максимум практики и минимум теории. Тот самый минимум перед вами.

Давайте еще раз взглянем на схему нашего приложения: приложение

В шапке слева заголовок и три кнопки выбора года. Ниже — фото соответствующего года, отсортированное по количеству лайков.

В шапке справа — ссылка войти/выйти.

Представим, как должны выглядеть данные для такой страницы:

app: {
    page: {
        year: 2016,
        photos: [photo, photo, photo...]
    },
    user: {
        name: Имя,
        ...
    }
}

Примите поздравление, мы только что описали как должно выглядеть состояние (state) нашего приложения.

За состояние нашего приложения отвечает объект Store. Как уже не раз упоминалось — это обычный объект. Важно, что в отличие от Flux, в Redux только один объект Store.

Не хочется оставлять вас надолго без практики, поэтому процесс создания store и немного подробностей про него будут аккуратно вплетены в следующие главы, а пока достаточно того, что: store, «объединяет» редьюсер (reducer) и действия (actions), а также имеет несколько чрезвычайно полезных методов, например:

  • getState() — позволяет получить состояние приложения;
  • dispatch(actions) — позволяет обновить состояние путем вызова действия;
  • subcribe(listener) — регистрирует слушателей

Actions

Actions описывают действия.

Actions — это простой объект. Обязательное поле — type. Также, если следовать соглашению, все данные, которые передаются вместе с действием, кладут внутрь свойства payload. Таким образом, для нашего приложения мы можем составить, например, такую пару actions:

{
    type: 'ЗАГРУЗИ_ФОТО',
    payload: 2016 // год
}
{
    type: 'ФОТО_ЗАГРУЖЕНЫ_УСПЕШНО',
    payload: [массив фото]
}

Чтобы вызвать actions, мы должны написать функцию, которая в рамках Flux/Redux называется — ActionsCreator (создатель действия), но перед этим стоит принять во внимание, что обычно тип действия описывают как константу. Например, константы вашего проекта:

const GET_PHOTO_REQUEST = 'GET_PHOTO_REQUEST'
const GET_PHOTO_SUCCESS = 'GET_PHOTO_SUCCESS'

Возникает вопрос — зачем? В маленьких проектах — незачем. В больших — это удобно. Пока, просто запомните.

Вернемся к ActionsCreator, один из наших «создателей действий» выглядел бы так:

function getPhotos(year) {
  return {
    type: GET_PHOTOS,
    payload: year
  }
}

Итого: actions сообщает нашему приложению — «Эй, что-то произошло! И я знаю, что именно!»


Reducer

«Actions описывает факт, что что-то произошло, но не указывает, как состояние приложения должно измениться в ответ, это работа для Reducer'а» — (официальная документация)

Наше приложение не нуждается в нескольких редьюсерах, но крайне необходимо познакомить читателя с reducer composition, так как это фундаментальный шаблон построения redux приложений: мы разбиваем наше глобальное состояние на кусочки, за каждый кусочек отвечает свой reducer. Кусочки объединяются в Корневом Редьюсере (rootReducer).

Схематично наше приложение можно представить так: приложение_редьюсер

Так как у нас есть reducer'ы page и user, можно представить следующий диалог:

pageActions: Пришло 123 фото
Reducer (page): OK, нужно положить эти 123 фото в page.photos

А на js выглядело бы так:

function page(state = initialState, action) {
  switch (action.type) {
    case GET_PHOTO_SUCCESS:
      return Object.assign({}, state, {
        photos: action.payload
      })
    default:
      return state
  }
}

Обратите внимание, мы не мутировали наш state, мы создали новый state. Это важно. Крайне важно. В редьюсере мы всегда должны возвращать новый объект, а не измененный предыдущий.

На практике мы будем использовать object spread syntax, поэтому предыдущую функцию с Object.assign можно переписать следующим образом:

function page(state = initialState, action) {
  switch (action.type) {
    case GET_PHOTO_SUCCESS:
      return {...state, photos: action.payload} // Object spread syntax
    default:
      return state
  }
}

Объект, который мы возвращаем в редьюсере, далее с помощью функции connect превратится в свойства для компонентов. Таким образом, если продолжить пример с фото, то можно написать такой псевдо-код:

<Page photos={reducerPage.photos} />

Благодаря этому внутри компонента <Page /> мы сможем получить фото, как this.props.photo


Здесь очень кратко освещена самая важная теория.

Если что-то осталось не понятным, не переживайте, на практике мы все закрепим, и тогда все встанет на свои места.

results matching ""

    No results matching ""