# Redux, менеджмент состояний ## О Flux ![Flux](https://facebook.github.io/flux/img/overview/flux-simple-f8-diagram-explained-1300w.png) Представляет методологию/архитектуру frontend-приложения, которая состоит из: 1. Actions (Действия) - имена событий, которые происходят в приложении. Обычно представлены строками 2. Диспетчер (Dispatcher) передаёт преобразованную информацию о действии хранилищу 3. Хранилище (Store) - централизованный источник значимой информации о состоянии приложения 4. Представление (View) - компонент, отвечающий за вывод информации пользователю ## Redux - не часть React Это самостоятельная библиотека, которую можно интегрировать в React или любой frontend-проект" ## Преимущества Redux - Единое состояние - Переиспользование данных - Упрощение отслеживания состояния ## Когда необходим Redux? - Крупный проект - Много значимой информации о состоянии приложения, которую необходимо переиспользовать ## Термины - Состояние (state). Параметры компонента или всего приложения - Редуктор (Reducer). Функция, которая преобразует информацию о текущем состоянии компонента и выполненном действии в новое состояние компонента ## Взаимодействие частей Redux ![](https://labs.tadigital.com/wp-content/uploads/2020/04/getting-started-with-redux-1096x453.png) В 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) ```