Что означает redux в названии игры
5 причин, почему вы должны забыть о Redux в приложениях на React
MobX позволяет писать более чистый и понятный код
Давайте сраним 2 фрагмента кода, которые делают тоже самое. Вот как выглядит редюсер в Redux:
Чтобы изменить состояние, вам нужно вызвать функцию, которая называется action в терминологии Редакс:
И в большинстве случаев (не всегда, но это «лучшие практики» которые используются на многих проектах) вам надо будет написать вот такой boilerplate:
Потом надо будет инициализировать store (это надо будет сделать один раз, но всё же):
И прокинуть наш заинициализированный стор дальше в приложение через провайдер (тоже одноразовая операция):
Теперь можно выполнять какие-то операции с данными в ваших компонентах:
Получается, что в случае с Redux, чтобы поменять данные в вашем хранилище, вы должны вызывать какие-то функции, которые будут создавать новый объект состояния… Лично по мне, это звучит как полная ерунда. Давайте глянем на тот же фунционал в исполнении MobX. Наш стор:
И дальше можно использовать в компоненте:
Да, всё верно, вместо каких-то функций, которые изменяют объекты, вы можете использовать классический ООП подход, с классами, их свойствами и методами. Пусть вас не пугают декораторы(@) внутри, они просто добавляют функционал, необходимый для отслеживания изменения данных. Кстати, похожий подход, с классами, для хранения данных используется в Angularjs (Скрин взят отсюда angular.io/start/data):
MobX позволяет писать меньше кода
Чтобы убедиться в этом просто посмотрите на примеры выше. И теперь, вместо написания бесконечного бойлерплейта, можно наконец-то сосредоточиться на написании бизнес логики приложения, что не может не радовать.
Третье — оптимизация производительности
Если посмотреть примеры выше, то можно увидеть, что в случае с MobX я не использовал pure component и это не ошибка. Вам просто не надо использовать никакую оптимизацию в этом случае, потому что ваш компонент будет перерендериваться только тогда, когда данные, которые вы в нем используете поменяются. И да, можно забыть о Pure Components, shouldComponentUpdate и что вы там ещё используете в этих случаях. В идеале, каждый ваш компонент, который не HOC и использует какие то данные из хранилища должен быть observable и тогда вы забудете о проблемах с оптимизацией навсегда.
Четвертое — меньше зависимостей
Тот кто использует Redux, должен не по наслышке знать, что в комплекте с ним идет множество «замечательных» билиотек. И хорошо, если это будет просто thunk, а может быть и так, разработчики пойдут по пути света тьмы и захотят использовать Redux Saga, Reslect и еще кучу непонятных библиотек, которые делают ваш код не только более медленным, но и более сложным в понимании. И допилить какой-то незначительный функционал или найти баг в этом произведении будет невероятно сложно и долго. MobX это ultimate решение == не требующее дополнительных библиотек, лишит вас всех этих прелестей, поэтому с ним бизнес логика вашего приложения будет чистой, как слеза младенца.
Пятая причина — возможность отказаться от setState
setState несёт в себе ряд недостатков (краткий перевод статьи которую можно в оригинале прочитать тут):
1. Он асинхронный.
Это может привести к неожидаемому поведению:
На скрине выше в алерте должно было быть 2, но так как setState асинхронный, он приходит позже.
2. setState ведет к ненужным перерендерам компонента:
а. Он перерендеривается, даже если новое значение == старому значению
б. Есть ситуации, когда изменение state не приведет ни к какому изменению, например, когда у нас есть условия для отбражения в котором стоит state. На скрине ниже при клике произошел перерендер, хотя данные не должны рендериться из-за условия fasle:
в. Иногда данные, которые обновляет setState вообще не играют роли в отрисовке DOM (например, таймеры). И всё равно происходит перерендер компонента.
3. setState подходит далеко не для всех случаев.
Есть компоненты, в которых используются хуки / методы жизенных циклов компонента и в этом случае будет не только происходить лишний перерендер, но эти события (хуки) будут вызываться каждый раз при этом, что может привести к странному поведению.
Использование MobX оградит вас от этих недостатков, ведь можно полностью отказаться от setState:
Когда, зачем и почему может быть полезным Redux
Фреймворков для веб-разработчиков много с разным функционалом, разного размера, для разных целей. Некоторые мастера предпочитают что-то одно, а другие – пробуют новое и ищут нечто интересное. Но почему же Redux остается популярным и востребованным? Что его отличает и для каких целей он подходит? Об этом в материале ниже.
Различных фреймворков много, благодаря чему перед разработчиками и верстальщиками рано или поздно встает вопрос: что лучше использовать в данном проекте, а что – нет, зачем может понадобиться именно та среда разработки, а не эта и так далее. Разумеется, что ответить на эти вопросы можно по-разному, информации об этом тоже множество. Но попробуем в этом материале разобраться в необходимости использования Redux, в причинах его востребованности и его особенностях.
Некоторые его любят, некоторые – уже не сильно, но сколько мастеров – столько и мнений. Управлять огромным потоком данных и их связями действительно было сложновато до появления фреймворка Redux. Но вдохновленный набором шаблонов программирования Flux Facebook, Redux предназначен для управления состоянием данных в приложениях JavaScript. Хотя он в основном используется с библиотекой React, многие веб-разработчики с успехом применяют его с иными фреймворками и библиотеками, такими как jQuery, Angular или Vue.
При очень небольшом размере (около 2 КБ, включая расширения) Redux гарантирует, что каждый компонент некоторого приложения может иметь прямой доступ к состоянию приложения без необходимости отправлять props (входные параметры) дочерним компонентам или использовать функции обратного вызова для отправки данных обратно.
В этом материале давайте посмотрим на то, как Redux глубоко укоренился в концепциях функционального программирования и каким образом можно решить, нужно ли веб-мастеру его использовать в своем приложении, описания интерфейса сайта.
Когда и зачем нужен Redux
Здравый смысл подсказывает, что нет смысла при каждом новом проекте постоянно запускать новые фреймворки и работать только в них, ведь их описывают как самые новые и блестящие инструменты для самых разных задач. Но разве компоненты не имеют своего состояния? Зачем вообще нужен этот вот самый инструмент для управления данным состоянием некоторого компонента?
Не поймите превратно, React велик и могуч и вполне можно в проекте обойтись только одним этим фреймворком и более ничего не использовать. Тем более, что мы уже рассматривали подробно React и приводили примеры веб-проектов, что использовали только эту библиотеку для создания функционала интерфейса (то же самое приложение, по сути). Но по мере того, как приложение становится более сложным, с большим количеством разных компонентов, использование всего лишь одного фреймворка для управления всем этим «массивом», объемом данных и так далее может стать очень проблематичным и осложненным действом. Проблем может появиться много, даже неудобств и излишней трудоёмкости.
И именно в этот момент Redux способен сэкономить время; он значительно уменьшает и упрощает сложности, возникающие в таких больших приложениях. Если у веб-разработчика есть опыт работы в React, он может великолепно понять, что поток данных React таков, что родительские компоненты передают props (входные параметры) дочерним компонентам. В огромном приложении с данными, что используются в разных компонентах, постоянно изменяемыми и сохраняемыми в хранилище, создающими множественные связи – рано или поздно сам код станет очень трудно читать и даже улучшать. Вам самим, в первую очередь.
Чтобы представлять, о чем идет речь, посмотрим на диаграмму ниже.
В React (как, впрочем, и в других фреймворках) связь между двумя компонентами, не имеющими отношения родитель-потомок (дочерний элемент), не рекомендуется. React обращает внимание, что если такое сделать (создать связь), можно вполне создать собственную глобальную систему событий по шаблону Flux; и именно в этот момент и появляется на сцене Redux.
С Redux у нас есть хранилище, в котором можно сохранять все состояния приложения. Если в компоненте A происходит изменение состояния, оно затем передается в хранилище, а другие компоненты B и C, которые должны знать об этом изменении состояния в компоненте A, могут получать эту самую информацию об этом изменении из хранилища:
Увидели? Это даже намного лучше, чем мы предполагали. Если бы наши компоненты взаимодействовали друг с другом, мы создали бы уязвимую и нечитаемую базу программного кода с множеством ошибок. Redux делает ситуацию другой, меняет её и совершенствует.
Компонент A отправляет изменения своего состояния в хранилище, и если Компонент B и C нуждаются в данных об этих изменениях состояния, они могут получить их из хранилища. Таким образом, логика потока данных является бесшовной.
Помимо своей основной миссии, Redux предоставляет и множество иных преимуществ при своем использовании. Посмотрим на основные три таких момента, что являются сами по себе особо важными среди прочих:
Имея только один источник данных (хранилище), у разработчиков становится мало проблем с синхронизацией текущего состояния компонентов (данных этого самого хранилища) с действиями и другими частями приложения.
Redux имеет строгие рекомендации о том, как должен быть организован код. Соответственно и, что разумно, это дополнительно обеспечивает предсказуемый результат, который упрощает управление кодом.
Работа с кодом в Redux включает в себя чистые функции, которые изолированы друг от друга, что коррелирует с золотым правилом написания тестируемого кода: написать небольшие функции, которые делают только что-то одно и являются независимыми.
Здорово и все просто. Если приложение сложное – используем Redux и забываем обо всем прочем. Но так ли это в действительности? Что если существуют некоторые особенности, когда Redux все же нужен?
Когда Redux может не потребоваться
Это может показаться некоторым читателям вполне очевидным, но мы все равно упомянем об этом. Не обязательно использовать Redux. Иногда имеет смысл и вовсе не делать этого. Если какой-либо из сценариев ниже верен для вас, вам, вероятно, вообще не нужен Redux:
Нашли что-то свое, тогда с большой вероятностью Redux может не потребоваться. И неважно, какое приложение, какая работа сайта и какой проект. Все зависит от их сложности, масштабности. Но что касается самого Redux, то этот фреймворк при своем весе около 2 КБ имеет весьма непростые возможности.
Redux в частях и под лупой
Действия (actions)
Это просто события, созданные с помощью функций для отправки данных из приложения в хранилище. Данные могут быть отправлены различными способами, такими как отправка формы, вызов API или обычного взаимодействия с пользователем. Каждое действие в Redux имеет свойство type, которое описывает тип действия, а также «важную» информацию, отправляемую в хранилище. Давайте рассмотрим самый простой пример действия (actions.js) в работе, размещенного на GitHub.
Чтобы вызвать действие в любом месте приложения, Redux использует метод dispatch(), который отправляет действия в хранилище Redux, указывающие на изменение состояния (dispatch.js)
Редюсеры (Reducer)
Редюсеры (Reducer) – это функции, которые считывают из хранилища текущее состояние приложения через отправленное действие, а затем возвращают новое состояние. Посмотрим на код ниже (handle-auth.js), который получает данные о текущем состоянии, а затем возвращает следующее состояние:
При создании более сложных приложений рекомендуется использовать метод combineReducers(). Этот метод объединяет все редюсеры в приложении в один список функций (index-reducer.js), где каждый из них обрабатывает свою часть состояния приложения, а параметр состояния отличается и является персональным для каждой функции.
Здесь также стоит отметить, что редюсеры должны быть написаны с чистыми функциями, особенности которых в том, что:
Хранилище
Хранилище похоже на сердце фреймворка Redux. Это единственный источник истины, в котором находятся все состояния приложения и который обеспечивает доступ к состоянию с помощью нескольких методов, действий отправки данных и регистрации записей. Любое отправленное действие возвращает новое состояние данных в хранилище с помощью редюсеров. И вот как выглядит код хранилища (create-store.js)
Функциональное программирование и Redux
Если вы уже определились и решили использовать Redux в своей работе, кроме всего вышесказанного необходимо знать, как работает функциональное программирование. Redux был основан на принципах функционального программирования и понимание его концепций даст яркое и четкое представление о том, как Redux работает в целом, и почему он способен делать именно то, что делает. Без понимания целостности фреймворка работать в нем сложно и относится такая мысль ко всем средам разработки без исключения.
Так что же это за принципы такие «функционального программирования»?
Функциональное программирование включает в себя написание более простых, меньших и изолированных функций. Следуя этой схеме работа с кодом, тестирование и отладка упрощаются. Поскольку функции малы и изолированы, это делает их многоразовыми в использовании. Вот и поэтому их можно копировать и вставлять в любое место, туда, где они необходимы.
Это также позволяет избежать ситуаций, когда необходимо писать больше кода. Но опять же, функциональное программирование требует понимания понятий, таких как чистые функции, анонимные функции, закрытие и функции первого класса. И это только часть нужных понятий.
Завершение
Это правда. Redux является большой библиотекой по управлению состоянием приложения. И так же, правда и то, что свою популярность фреймворк заслужил. Но что особенно может быть интересным, что Redux успешно применяется в таких проектах, как WordPress, аналогично тому, как RedBox нашел применение в Uber и Twitter. И еще одна правда заключается в том, что Redux не слишком-то подходит для каждого конкретного приложения.
Приложения, выполняющие, в основном, простые действия и не требующие рендеринга (обработки) на стороне сервера, вероятно, не нуждаются в Redux; их действия можно обрабатывать на уровне компонентов. Но в любом случае, Redux – отличный инструмент, который стоит попробовать тем, кому нравится React; если уже знакомы с React и умеете в нем работать.
Впрочем, как и всегда, некоторые полагают Redux устаревшим для работы с данными. Но тут уж, кому и что ближе, а факты – они просто есть.
Введение в Redux & React-redux
Оглавление
Введение
Вот вы прочитали мою статью про React (если нет, то настоятельно рекомендую вам сделать это) и начали разрабатывать приложения на нём. Но что это? Вы замечаете, как с расширением вашего приложения становится всё сложнее следить за текущим состоянием, сложно следить за тем, когда и какие компоненты рендарятся, когда они не рендарятся и почему они не рендарятся, сложно следить за потоком изменяющихся данных. Для этого и есть библиотека Redux. Сам React хоть и лёгкий, но для комфортной разработки на нем нужно много чего изучить.
И сегодня мы разберём 2 библиотеки: Redux и React-redux. Для использования Redux’а вам не нужно скачивать дополнительных библиотек, но, если использовать его в связке с библиотекой React-redux разработка становится ещё удобнее и проще.
Все примеры из этой статьи вы можете найти в этом репозитории на Github. Там находится полностью настроенное приложение React с использованием Redux и React-redux. Вы можете использовать его как начальную точку для вашего проекта. Изменяйте названия файлов и добавляйте новые в этот репозитории для создания собственного приложения. Смотрите во вкладку релизы для того что бы найти разные версии приложения. Первая содержит приложение только с использованием Redux, второе с использованием Redux и React-redux.
Мотивация использования Redux
Механизм локального хранилища компонента, который поставляется вместе с базовой библиотекой (React) неудобен тем, что такое хранилище изолировано. К примеру, если вы хотите, чтобы разные независимые компоненты реагировали на какое-либо событие, вам придётся либо передавать локальное состояние в виде пропсов дочерним компонентам, либо поднимать его вверх до ближайшего родительского компонента. В обоих случаях делать это не удобно. Код становится более грязным, трудночитаемым, а компоненты зависимыми от их вложенности. Redux снимает эту проблему так как всё состояние доступно всем компонентом без особых трудностей.
Redux является универсальным средством разработки и может быть использован в связке с различными библиотеками и фреймворками. В этой же статье будет рассматривается использование Redux в React приложениях.
1. Установка Redux и начало работы
Используете ли вы Yarn или Npm, выполните одну из этих команд для установки Redux:
Скорее всего вы используете папку src в которой хранится ваша кодовая база. Файлы, связанные с redux принято хранить в отдельной папке. Для этого я использую папку /src/store в которой хранится всё то, что связано с Redux и хранилищем приложения. Вы можете назвать ее по другому или поместить в другое место.
Создайте базовую структуру для хранилища. Она должна выглядит примерно следующим образом:
.store
├── actionCreators
│ ├── action_1.js
│ └── action_2.js
├── actions
│ ├── action_1.js
│ └── action_2.js
├── reducers
│ ├── reducer_1.js
│ ├── reducer_2.js
│ └── rootReducer.js
├── initialState.js
└── store.js
Конечно здесь я использовал примитивные названия для файлов, это сделано для наглядности. В настоящем проекте так называть файлы не стоит.
2. Redux
2.1 createStore
Когда вы создали базовую структуру для работы с хранилищем Redux пришло время понять то как вы можете взаимодействовать с ним.
Глобальное хранилище приложения создаётся в отдельном файле, который как правило называется store.js:
2.2 reducer()
reducer — чистая функция которая будет отвечать за обновление состояния. Здесь реализовывается логика в соответствие с которой будет происходить обновление полей store.
Так выглядит базовая функция reducer:
Функция принимает значение текущего состояния и обьект события (action). Обьект события содержит два свойства — это тип события (action.type) и значение события (action.value).
К примеру если нужно обработать событие onChange для поля ввода то объект события может выглядеть так:
Некоторые события могут не нуждаться в передаче каких-либо значении. К примеру, обрабатывая событие onClick мы можем сигнализировать о том, что событие произошло, более никаких данных не требуется, а как на него реагировать будет описывать логика, заложенная непосредственно в сам компонент которой должен на него реагировать и частично в reducer. Но во всех случаях необходимо определять тип события. Редьюсер как бы спрашивает: что произошло? actio.type равен «ACTION_1» ага значит произошло событие номер 1. Дальше его нужно как то обработать и обновить состояние. То, что вернёт редьюсер и будет новым состоянием.
ACTION_1 и ACTION_2 это константы событий. По-другому Actions. Про них мы поговорим далее 2.5 Actions.
Как вы уже догадались store может хранить сложную структуру данных состоящих из набора независимых свойств. Обновление одного свойства оставит нетронутым другие свойства. Так из примера выше, когда происходит событие номер один (ACTION_1) обновляется поле номер один (value_1) в store при этом поле номер два (value_2) остаётся нетронутым. В общем механизм схож с методом this.setState().
2.3 dispatch()
Что бы обновить store необходимо вызвать метод dispatch(). Он вызывается у объекта store который вы создаёте в store.js. Этот объект принято называть store поэтому обновление состояния в моём случае выглядит так:
ACTION_1 это константа события о которой речь пойдет дальше (см. Actions).
Эта функция вызовет функцию reducer который обработает событие и обновит соответствующие поля хранилища.
2.4 actionCreator()
На самом деле передавать объект события напрямую в dispatch() является признаком плохого тона. Для этого нужно использовать функцию под названием actionCreator. Она делает ровно то что и ожидается. Создаёт событие! Вызов этой функции нужно передавать как аргумент в dispatch а в actionCreator передавать необходимое значение (value). Базовый actionCreator выглядит следующим образом:
Таким образом вызов dispatch должен выглядеть так:
С использованием actionCreator код становится более чистым.
2.5 Actions
actions это константы, описывающие событие. Обычно это просто строка с названием описывающее событие. К примеру константа описывающее событие номер один будет выглядеть следующем образом:
Опять же в проекте вам стоит называть константы в соответствии с событием, которое она описывает: onClick, createUserSesion, deleteItem, addItem и т.д. Главное, чтобы было понятно. Замете что я нигде не писал import поэтому не забудьте импортировать ваши константы перед их использованием. Потому что константы тоже принято разбивать на отдельные файлы храня их в специальной папке. Хотя некоторые хранят их в одном файле под названием actionTypes.js. Такое решение нельзя назвать не правильным, но и не идеальным.
2.6 getState()
С помощью dispatch() обновили, а как теперь посмотреть новое значение store? Ничего изобретать не нужно, есть метод getState(). Он также, как и метод dispatch вызывается на экземпляре объекта store. Поэтому для моего примера вызов
вернёт значение полей хранилища. К примеру что бы посмотреть значение поля value_1 необходимо будет вызвать
2.7 subscribe()
А как же узнать, когда состояние обновилось? Для этого есть метод subscribe(). Он также вызывается на экземпляре store. Данный метод принимает функцию, которая будет вызывается каждый раз после обновления store. Он как бы «подписывает» функцию, переданную ему на обновление. К примеру следующий код при каждом обновлении (при каждом вызове dispatch()) будет выводить новое значение store в консоль.
Этот метод возвращает функцию unsubscribe(). Которая позволяет «отписаться от обновления». К примеру если компонент удаляется из DOM стоит отписать его методы от обновления в componentWillUnmount(). Этот метод жизненного цикла вызывается при размонтировании компонента и это именно то место где стоит отписываться от обновления. Проще говоря в деструкторе.
2.8 combineReducers()
combineReducers() позволяет объединить несколько редьюсеров в один.
Если логика обновления компонентов довольно сложна и\или необходимо обрабатывать большое количество различных типов событий, то корневой reducer может стать слишком громоздким. Лучшим решением будет разбить его на несколько отдельных редьюсеров каждый из которых отвечает за обработку только одного типа событий и обновления определённого поля.
Когда вы разбиваете базовый редьюсер на несколько, то название каждого из них должно соответствовать полю которое он обновляет в store.
К примеру если редьюсер обновляет поле номер один, то он может выглядеть так:
Название редьюсера (value_1) показывает какое свойство он будет обновлять в store. Если переименуете его в value_2 то он станет обновлять value_2. Поэтому учтите это!
Когда используется единый редьюсер мы показываем какое поле хотим обновить:
Но когда вы разделили ваши редьюсеры вам нужно просто вернуть новое значение:
Поскольку здесь не требуется указывать которое из полей обновляет редьюсер ибо его название и есть поле которое он обновляет.
2.9 initialState
initialState — объект, представляющий начальное состояние хранилища. Он является вторым не обязательным аргументом метода createStore(). С созданием хранилища можно сразу объявить начальное состояние для его полей. Этот объект желательно создавать, даже в тех случаях, когда объявления начального состояния не требуется. Потому что этот объект помогает посмотреть на структуру хранилища и название его полей. Обычный объект initialState выглядит следующим образом:
В некоторых случаях (когда компонент сразу использует значение из store), его объявление может стать обязательным иначе вы получите ошибку: TypeError: Cannot read property ‘value_1’ of undefined.
Также редьюсеры всегда должны возвращать по дефолту текущее состояние. К примеру, если используется единый reducer то последнее значение в switch должно выглядеть так:
Если же вы разделяете редьюсеры на независимые функции, то он должен возвращать значение того свойства за которое он отвечает:
Также если вы не передаёте объект initialState в createStore вы можете вернуть его из редьюсера. В обоих случаях будет инициализировано начальное состояние для store.
3. React-redux
Казалось бы, у нас есть всё что бы использовать Redux. Но на деле использование его без пакета React-redux в React приложениях выглядит не очень красиво.
3.1 Provider
Для использование store в компоненте вам необходимо передавать его в пропсы:
И после использовать в компоненте: this.props.state. Для этого react-redux предостовляет метод Provider:
Также можно передать store напрямую в компонент, не оборачивая его в Provider и это будет работать. Но лучше всё-таки используйте Provider.
3.2 mapStateToProps()
Этот метод вызывается всякий раз, когда происходит обновление store и именно он передаёт необходимые свойства из store в компонент. К примеру компонент, должен реагировать и обновлять UI каждый раз, когда поле номер один (value_1) обновилось. На обновление других полей ему реагировать не нужно. Если вы не используете React-redux вам бы пришлось использовать метод subscribe() что бы узнавать об обновлении и далее каким то образом проверять обновилось ли поле номер один или нет. В общем несложно понять, что такой код будет выглядеть слишком грязным и избыточным. С помощью mapStateToProps() можно чётко определить какие поля интересуют компонент. И на какие поля он должен реагировать.
Возвращаясь к примеру выше, если компоненту один нужно получать поле номер один (value_1) то mapStateToProps для него будет выглядеть следующим образом:
После внутри компонента мы можем обращается к полю value_1 через this.props.value_1. И каждый раз когда это поле будет обновляется компонент будет рендерится заново.
Вы можете создать отдельную папку в /src/store для хранения файлов каждый из которых будет содержать функцию mapStateToProps для всех ваших компонентов. Либо (как сделал это я) использовать единую функцию возвращающую функцию mapStateToProps для каждого компонента. Лично мне нравится такой подход. Такая функция выглядит следующим образом:
Эта функция в качестве аргумента принимает строку с названием компонента и возвращает функцию mapStateToProps которая возвращает объект со свойством из store необходимом для данного компонента. Эту функцию можно назвать mapStateToPropsGenerator().
3.3 mapDispatchToProps()
Эта функция передаёт в компонент методы для обновления необходимого поля store. Что бы не вызывать dispatch напрямую из компонента вы будете использовать данный метод для того что бы передавать в props метод вызов которого приведёт к вызову dispatch и обновлению соответствующего поля. Просто теперь это будет выглядеть более элегантно, а код более понятным и чистым.
К примеру компонент, номер один должен иметь возможность обновлять поле номер один из store. Тогда mapDispatchToProps для него будет выглядеть следующим образом:
Теперь для обновления свойства value_1 вы будете вызывать changeValue_1() через this.props.changeValue_1(value). Не вызывая dispatch напрямую через this.props.store.dispatch(action_1(value)).
bindActionCreators следует импортировать из redux. Он позволяет оборачивать функцию dispatch и actionCreator в единый объект. Вы можете не использовать bindActionCreators но тогда код будет выглядеть избыточным. Вы должны старятся реализовать какую-либо функциональность так, чтобы код выгладил просто и миниатюрно. Поэтому ничего лишнего писать не следует.
Только чистый и понятный код. Метод bindActionCreators(actionCreator, dispatch) принимает два обязательных параметра это функцию actionCreator о которой мы уже говорили и dispatch. Возвращая метод для изменения полей store.
Также как и для mapStateToProps я использую функцию генератор возвращающую функцию mapDispatchToProps для каждого компонента:
3.4 connect()
Ну и теперь кульминация! То без чего всё это не будет работать. Это функция connect.
Именно она связывает mapStateToProps и mapDispatchToProps с компонентом и передает необходимые поля и методы в него. Возвращает она новый компонент-обёртку для вашего компонента. Как правильно именовать такой компонент я не знаю, ибо в самой документации React-redux это не описывается. Лично я добавляю окончание _w для компонентов оберток. Как бы _w = wrap Component. Подключение компонента в этм случае выглядит так:
И теперь в ReactDOM.render() вы передаёте не ваш компонент, а тот что возвращает функция connect.
Если же у компонента нет необходимости в передаче ему mapStateToProps или mapDispatchToProps передавайте undefined или null в него.