Основы Redux (теория)
ОБНОВЛЕНИЕ 2018: Вышло второе издание (современный код и версии пакетов, данное издание УСТАРЕЛО)
На канале так же проводятся бесплатные вебинары, публикуются переводы и авторские материалы, присоединяйтесь!
Основы 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): Ок, нужно положить эти 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
Я постарался очень кратко дать самую важную теорию.
Если что-то осталось не понятным, не переживайте, на практике мы все закрепим и тогда все встанет на свои места.
Last updated
Was this helpful?