Комбинирование редьюсеров

ОБНОВЛЕНИЕ 2018: Вышло второе издание (современный код и версии пакетов, данное издание УСТАРЕЛО)

На канале так же проводятся бесплатные вебинары, публикуются переводы и авторские материалы, присоединяйтесь!

Комбинирование редьюсеров

Зачем? Когда наше приложение разрастается, хочется еще больше модульности, чтобы каждый кусочек кода отвечал за конкретную часть. Так же и с редьюсерами, мы можем разбить наш главный редьюсер на несколько более мелких, и с помощью combineReducers из пакета redux собрать их воедино. Причем, абсолютно никакой магии, combineReducers просто возвращает "составной" редьюсер.

Для нашего приложения, можно выделить следующие reducer'ы (согласно схеме):

  • user

  • page

Создадим их:

src/reducers/user.js

const initialState = {
  name: 'Аноним'
}

export default function user(state = initialState) {
  return state
}

src/reducers/page.js

const initialState = {
  year: 2016,
  photos: []
}

export default function page(state = initialState) {
  return state
}

Обновим точку входа для редьюсеров:

src/reducers/index.js

import { combineReducers } from 'redux'
import page from './page'
import user from './user'

export default combineReducers({
  page,
  user
})

Обратите внимание, что структура объекта, которым можно описать все состояние нашего приложения - не изменилась. Все осталось также.

{
    user: {             
        name: 'Аноним'  
    }                   
    page: {             
        year: 2016,     
        photos: []      
    }                   
}

Тем не менее, в браузере у нас нерабочее приложение. В чем же проблема?

Ответ кроется в работе функции connect и в функции mapStateToProps из нашего файла App.js. Сейчас у нас там написано следующее:

function mapStateToProps (state) {
  return {
    user: state
  }
}

Что можно перевести так: возьми полностью "стейт" приложения и присоедини его в переменную user, дабы она была доступна из компонета App.js как this.props.user

Здесь, я предложу простую задачку на понимание происходящего. Измените компонент App.js и функцию mapStateToProps так, чтобы получить следующую картину:

Ответ:

src/containers/App.js

import React, { Component } from 'react'
import { connect } from 'react-redux'

class App extends Component {
  render() {
    const { name } = this.props.user // (1)
    const { year, photos } = this.props.page // (2)
    return <div>
      <p>Привет, {name}!</p>
      <p>У тебя {photos.length} фото за {year} год</p>
    </div>
  }
}

function mapStateToProps (state) {
  return {
    user: state.user, // (1)
    page: state.page // (2)
  }
}

export default connect(mapStateToProps)(App)

Сносками (1) и (2) я пометил связь.

Супер, сейчас у нас в user - попадет все из нашего приложения, что будет связано с пользователем , а в page - попадет все что связано с отображением соответствующего блока (год и массив фото).

Второй раз за главу, возникает вопрос - зачем? Ответ прежний: модульность, меньший объем кода в каждом файле и лучшая читаемость. А главное, мы так добьемся меньшего количества ненужных обновлений, представьте: пользователь кликнул на кнопку "2015", и обновился блок page, при этом блок user остался бы не тронутым вообще, если бы он являлся отдельным компонентом.

Нам ничего не мешает исправить это. Продолжим в следующей главе.

Исходный код на текущий момент.

Полезные ссылки:

Last updated