# Redux, менеджмент состояний
## О Flux

Представляет методологию/архитектуру frontend-приложения, которая состоит из:
1. Actions (Действия) - имена событий, которые происходят в приложении. Обычно представлены строками
2. Диспетчер (Dispatcher) передаёт преобразованную информацию о действии хранилищу
3. Хранилище (Store) - централизованный источник значимой информации о состоянии приложения
4. Представление (View) - компонент, отвечающий за вывод информации пользователю
## Redux - не часть React
Это самостоятельная библиотека, которую можно интегрировать в React или любой frontend-проект"
## Преимущества Redux
- Единое состояние
- Переиспользование данных
- Упрощение отслеживания состояния
## Когда необходим Redux?
- Крупный проект
- Много значимой информации о состоянии приложения, которую необходимо переиспользовать
## Термины
- Состояние (state). Параметры компонента или всего приложения
- Редуктор (Reducer). Функция, которая преобразует информацию о текущем состоянии компонента и выполненном действии в новое состояние компонента
## Взаимодействие частей Redux

В DOM мы можем создавать свои события. Действие в Redux напоминает тип события, например click, submit, mouseover
Действием может быть что угодно: число, строка, объект. Чтобы различать действия, им даются уникальные значения.
Например:
```js
{
type: 'SELECTED_USER',
userId: 175
}
```
Такая форма действия (объект со свойством type) является общепринятой и используется в сторонних библиотеках. Названия (типы) событий вы придумываете свои собственные (хоть BOBIK).
Все остальные свойства выбираются произвольно. В примере выше userId помогает понять, какой пользователь выбран
Когда надо (В приложении выбрали пользователя), через функцию dispatch действие передаётся диспетчеру
```js
dispatch({
type: 'SELECTED_USER'
})
```
dispatch - функция хранилища, входящая в состав Redux
Диспетчер преобразует его через редуктор в новое состояние:
```js
const usersReducer = (state, action) => {
switch (action.type) {
case 'SELECTED_USER':
return {
userId: action.userId
}
default:
return state
}
}
```
Соблюдайте принцип иммутабельности: возвращайте только новые объекты/массивы, не модифицируя исходные
Редуктор должен быть связан с хранилищем (чтобы подключить обработчик, менять хранилище при возникновении действия). Это можно сделать через функцию createStore, входящую в redux
```js
let store = createStore(usersReducer, defaultStoreValue)
```
При желании, можно передать в defaultStoreValue исходное состояние хранилища
## Установка без React
```html
<!DOCTYPE html>
<html>
<head>
<title>Redux basic example</title>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
</head>
<body>
<div>
<p>
Clicked: <span id="value">0</span> times
<button id="increment">+</button>
<button id="decrement">-</button>
<button id="incrementIfOdd">Increment if odd</button>
<button id="incrementAsync">Increment async</button>
</p>
</div>
<script>
function counter(state, action) {
if (typeof state === 'undefined') {
return 0
}
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
var store = Redux.createStore(counter)
var valueEl = document.getElementById('value')
function render() {
valueEl.innerHTML = store.getState().toString()
}
render()
store.subscribe(render)
document.getElementById('increment')
.addEventListener('click', function () {
store.dispatch({ type: 'INCREMENT' })
})
document.getElementById('decrement')
.addEventListener('click', function () {
store.dispatch({ type: 'DECREMENT' })
})
document.getElementById('incrementIfOdd')
.addEventListener('click', function () {
if (store.getState() % 2 !== 0) {
store.dispatch({ type: 'INCREMENT' })
}
})
document.getElementById('incrementAsync')
.addEventListener('click', function () {
setTimeout(function () {
store.dispatch({ type: 'INCREMENT' })
}, 1000)
})
</script>
</body>
</html>
```
В данном примере с помощью
```js
var store = Redux.createStore(counter)
```
Создаётся хранилище, куда передаётся ссылка на редуктор counter. На основе текущего состояния и возникающих действий он должен вернуть новое состояние
```js
function counter(state, action) {
if (typeof state === 'undefined') {
return 0
}
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
```
При любом действии вызывается counter и обязан вернуть новое значение состояния счётчика. Впоследствие это значение сохранятеся в оперативной памяти. После перезагрузки страницы состояние обнулится, но можно сохранить значение например в localStorage.
При возникновении любого из событий на странице, вызывается метод dispatch хранилища:
```js
document.getElementById('increment')
.addEventListener('click', function () {
store.dispatch({ type: 'INCREMENT' })
})
document.getElementById('decrement')
.addEventListener('click', function () {
store.dispatch({ type: 'DECREMENT' })
})
document.getElementById('incrementIfOdd')
.addEventListener('click', function () {
if (store.getState() % 2 !== 0) {
store.dispatch({ type: 'INCREMENT' })
}
})
document.getElementById('incrementAsync')
.addEventListener('click', function () {
setTimeout(function () {
store.dispatch({ type: 'INCREMENT' })
}, 1000)
})
```
Каждый раз, когда в хранилище что-либо происходит, вызывается функция-подписчик (subscriber). Подписка осуществляется с помощью
```
store.subscribe(render)
```