Комбинирование редьюсеров
Зачем? Когда наше приложение разрастается, хочется еще больше модульности, чтобы каждый кусочек кода отвечал за конкретную часть. Так же и с редьюсерами, мы можем разбить наш главный редьюсер на несколько более мелких, и с помощью combineReducers из пакета redux собрать их воедино. Причем, абсолютно никакой магии, combineReducers просто возвращает "составной" редьюсер.
Для нашего приложения, можно выделить следующие reducer'ы (согласно схеме из предыдущих глав):
  • user
  • page
Создадим их:
src/reducers/user.js
1
const initialState = {
2
name: 'Аноним',
3
}
4
5
export function userReducer(state = initialState) {
6
return state
7
}
Copied!
src/reducers/page.js
1
const initialState = {
2
year: 2018,
3
photos: [],
4
}
5
6
export function pageReducer(state = initialState) {
7
return state
8
}
Copied!
Обновим точку входа для редьюсеров:
src/reducers/index.js
1
import { combineReducers } from 'redux'
2
import { pageReducer } from './page'
3
import { userReducer } from './user'
4
5
export const rootReducer = combineReducers({
6
page: pageReducer,
7
user: userReducer,
8
})
Copied!
Обновим configureStore:
src/store/configureStore.js
1
import { createStore } from 'redux'
2
import { rootReducer } from '../reducers'
3
4
// удалили "начальное состояние = initial state"
5
// так как теперь наш редьюсер составной,
6
// и нам нужны initialState каждого редьюсера.
7
// Это будет сделано автоматически.
8
export const store = createStore(rootReducer)
Copied!
Посмотрим что у нас теперь "консолится" в компоненте <App />, а так же в React dev tools.
broken-app-with-combineReducer
Сейчас в браузере у нас нерабочее приложение. В чем же проблема?
Ответ кроется в работе функции connect и в функции mapStateToProps из нашего файла App.js. Сейчас у нас там написано следующее:
1
const mapStateToProps = store => {
2
console.log(store)
3
return {
4
user: store.user,
5
}
6
}
Copied!
Что можно перевести так: возьми полностью "стор" приложения и присоедини его в переменную user, дабы она была доступна из компонета App.js как this.props.user
Здесь, я предложу простую задачку на понимание происходящего. Измените компонент App.js и функцию mapStateToProps так, чтобы получить следующую картину:
hello-anonymous-zero-photos
Ответ:
src/containers/App.js
1
...
2
class App extends Component {
3
render() {
4
const { user, page } = this.props
5
return (
6
<div className="App">
7
<header className="App-header">
8
<h1 className="App-title">Мой топ фото</h1>
9
</header>
10
<p>Привет, {user.name}!</p>
11
<p>
12
У тебя {page.photos.length} фото за {page.year} год
13
</p>
14
</div>
15
)
16
}
17
}
18
19
const mapStateToProps = store => {
20
console.log(store)
21
return {
22
user: store.user,
23
page: store.page,
24
}
25
}
26
...
Copied!
Работа функции mapStateToProps многих вводит в ступор. В данной функции, мы хотим отрезать от нашего общего пирога (Store) только те кусочки (редьюсеры), которые нам нужны.
Еще можно применить аналогию: мы приклеиваем в props компонента, данные из тех редьюсеров, которые нам требуются.
А если быть более точным, то мы не только получаем в this.props.XXX данные, которым нам нужны, но мы еще и подписываемся на изменение этих данных.
После того, как вы знаете о подписке, пора вам раскрыть еще один козырь - когда мы подписываемся только на нужные редьюсеры в компоненте, перерисовка происходит только в случае изменения конкретно этих данных. Если же мы бы подписались просто на весь корневой редьюсер, то не важно в каком бы редьюсере изменились данные - все подписанные на корневой редьюсер компоненты обновились бы.
Опять же, в теории это все абсолютно не "зайдет" не подготовленному читателю. Поэтому на практике мы еще не раз разберем данную информацию.
Итого: сейчас у нас в user - попадет все из нашего приложения, что будет связано с пользователем , а в page - попадет все что связано с отображением соответствующего блока (год и массив фото).
Исходный код на текущий момент.
Полезные ссылки:
Copy link