Хотите знать, как реализовать редукцию без дополнительного веса? Устали от руководств, объясняющих вам, как использовать что-то в дополнение к трем другим пакетам, и с небольшим объяснением необходимого, но раздутого шаблонного кода?

Не говори больше, я тебя прикрою.

Прежде всего, вам может не понадобиться Redux. Это так просто. Но раз уж вы здесь, давайте погрузимся и посмотрим, как его использовать. Я разделю этот урок на несколько частей. В этой части я объясню, КАК использовать избыточность настолько просто, насколько это возможно, в то время как последующие действия зарезервированы для более сложных сценариев и некоторые объяснения определенных решений в моем рабочем процессе и почему я думаю, что это хороший способ делать что-то так или иначе.

Как обычно, у меня есть код, подготовленный для этого урока, поэтому, если вы перейдете на GitHub, вы можете получить мои настройки по этому тегу. Я создал простую копию игры с кликером, которую я назвал копировать кликер. Каждый раз, когда пользователь нажимает на дискету, он создает нашу виртуальную валюту копий и, используя указанную валюту, может увеличивать CpC (количество копий за клик), чтобы получить больше отдачи от своих кликов. Мы будем использовать Redux для перемещения состояния приложения из кода реакции в хранилище Redux.

Начиная с простого приложения React, нам нужно 2 пакета npm для работы с Redux. В то время как сам Redux создает хранилище для наших данных и дает нам возможность его обновлять, react-redux позволяет нам подключить наш магазин Redux к нашему приложению для реагирования. Redux довольно невежественен в отношении своей среды и может использоваться с простым javascript или любой другой библиотекой или фреймворком вместо React.

Откройте код из моего проекта GitHub и перейдите в корень проекта, чтобы установить наши пакеты в командной строке через npm или yarn.

›› npm i redux

Для начала давайте удалим текущее состояние из нашего приложения, а также весь код, который переводит действия пользователя в изменения состояния. Через мгновение мы заменим его нашим новым магазином избыточности.

Мы также будем получать наши копии и CpC (CopiesperClick) от нашего реквизита, а не от состояния. Вы можете передать их из app.jsx в качестве реквизита, но мы все равно подойдем к этому через несколько строк.

Магазин Redux работает по простому принципу. Хранилище обрабатывает ваши данные, и вы можете читать из указанного хранилища, но вы не можете изменить его напрямую. Для этого вы отправляете действие, а редюсер читает действие и изменяет данные для вас. Таким образом, ваш магазин Redux представляет собой единую точку истины. Вы отправляете действия, а магазин сокращает их. Просто как тот.

Переходя к нашему app.jsx, давайте создадим наш магазин, используя функцию createStore из Redux. Мы также напишем наш первый редюсер для обработки кликов на нашем CopyClicker.

Редьюсер ожидает два параметра, состояние и действие, и возвращает состояние, когда оно выполнено. Поскольку наш редьюсер передает свое состояние в хранилище, а мы хотим заранее установить наши начальные значения, мы создадим простой объект для нашего начального состояния и передаем его нашему редюсеру в качестве значения по умолчанию. Чтобы проверить, работает ли наш код, мы можем прочитать текущее состояние из хранилища с помощью функции store.getState().

Чтобы лучше отслеживать и понимать, что представляет собой часть действия нашего редуктора, давайте добавим его в наш console.log. На данный момент вы увидите только одно действие с типом, начинающимся с «@@redux/INIT», так что это то, что делает хранилище при его первоначальном создании. Давайте отправим наше собственное действие с типом COPY_CLICK, так как это то, что мы сейчас пытаемся реализовать.

Как видите, наше отправленное действие передается маршрутизатору, но в данный момент наш маршрутизатор не знает, что с ним делать. Давайте изменим это. Случай переключения поможет нам отфильтровать только те типы действий, которые мы прослушиваем и принимаем по умолчанию, возвращая исходное состояние. Возврат исходного состояния по умолчанию важен, так как вы можете использовать несколько редьюсеров в избыточности, и каждый из них либо передает старое состояние (по умолчанию), либо новое состояние.

Здесь важно отметить, что редюсер всегда должен возвращать либо неизменное текущее состояние, либо новый объект состояния. Мы никогда не должны изменять объект состояния. Если редюсер запускается действием, он должен создать новый объект состояния как копию старого состояния с измененными некоторыми или всеми значениями. Здесь важна неизменность, чтобы избыточность работала должным образом и предотвращала ошибки, которые трудно отследить.

В случае, если наш тип действия «COPY_CLICK» найден, мы увеличим текущее количество копий в состоянии на единицу и передаем этот новый объект состояния обратно в качестве возвращаемого значения. Если мы добавим еще один console.log после отправки, мы увидим, что состояние действительно изменилось.

Если бы вы дважды продублировали нашу строку кода store.dispatch(), результирующее состояние было бы 3 копии вместо 1.

Теперь мы можем начать передавать эти значения нашему компоненту CopyClicker. Здесь вступает в игру второй установленный нами пакет npm. React-redux предлагает нам компонент Provider и connect. Провайдер оборачивает наше приложение и позволяет нам сделать наш магазин доступным для остальной части нашего приложения. Мы просто помещаем его в наш текущий код приложения и передаем в наш магазин в качестве реквизита.

Connect, с другой стороны, позволяет нам подключать части нашего приложения к предоставленному хранилищу. Для этого мы можем обернуть наш компонент в компонент соединения более высокого порядка.

Как компонент более высокого порядка, connect ожидает от нас чего-то и возвращает функцию, которая затем принимает наш компонент CopyClicker для подключения к хранилищу. На данный момент connect не делает никаких предположений о том, что именно должно быть подключено, а только предлагает нам текущее состояние. Нам нужно предоставить этот объект сопоставления самостоятельно, поэтому давайте напишем функцию, которая принимает состояние и соответствующим образом отображает наши реквизиты.

Поскольку мы передаем состояние нашей стрелочной функции, теперь мы можем напрямую обращаться к state.copies, сопоставлять его с нашей ранее существовавшей опорой «copies» и возвращать объект mapStateToProps, который мы можем передать нашему подключению. Это означает, что теперь мы больше не экспортируем компонент CopyClicker напрямую, а его подключенную версию.

В зависимости от типа проекта, над которым вы работаете, вы, скорее всего, напрямую подключите к магазину только несколько компонентов. Старайтесь, чтобы остальные компоненты были как можно более тупыми. Это эмпирическое правило, которое хорошо сработало для меня.

Прямо сейчас вы должны увидеть, что наше приложение показывает количество копий как 1 (или 3, если вы ранее оставили все 3 диспетчерских вызова), но кнопки еще не работают. Нам также нужно подключить наши кнопки внутри компонента CopyClicker к хранилищу.

Прежде всего, избавьтесь от вызова диспетчера в App.jsx и всех console.logs, кроме того, что находится внутри редьюсера, затем снова вернитесь к нашему CopyClicker.jsx. До сих пор мы использовали диспетчеризацию только в том месте, где определяли хранилище. К счастью для нас, connect добавляет функцию отправки к нашей пропе, поэтому мы можем использовать ее через props.dispatch(). Простым решением было бы сделать это внутри нашей функции onCopyClicked.

Если вы сделали это прямо сейчас, вы сможете увеличить количество копий на 1 за каждый щелчок по значку дискеты и увидеть наше действие в консоли, как и раньше.

Есть еще одна вещь, которую я хотел бы сделать сейчас, которая не является на 100% необходимой, но для меня это то, что я предпочитаю делать, чтобы сохранить чистоту и удобство чтения. Прокрутите вниз до строки, где мы создали наш объект mapStateToProps, и создайте второй объект с именем mapDispatchToProps. Как и раньше с отображаемым состоянием, теперь мы добавляем к нашему объекту новое свойство с именем copyClicked и сопоставляем его с нашей функцией отправки. Не забудьте также добавить эту новую опору в наши propTypes как PropTypes.func.isRequired. Мы можем использовать эту новую функцию copyClicked() из нашего реквизита для обработки пользовательского ввода.

Хотя этот шаг не является обязательным, он позволяет нам сохранить наш код чистым и читабельным и ограничивает взаимодействие с хранилищем компонентом подключения, не распространяя его на весь исходный компонент CopyClicker.

И это все. Мы успешно добавили избыточность в наше приложение и подключили наше состояние и нашу функциональность к хранилищу. Мы можем еще немного почистить наш код и удалить последний оставшийся файл console.log, и все готово.

"Но эй!" вы можете сказать: «Я больше не могу повышать цену за клик. Мы упустили это!». Вы абсолютно правы. Я зарезервировал этот фрагмент для следующей части этой серии руководств. Моя вина!

Резюме и прогноз:

Мы использовали Redux для создания объекта хранилища, определили редюсер, который прослушивает наши действия, а затем подключили наше приложение к React-Redux, поместив код приложения в наш провайдер и подключив компонент, который должен взаимодействовать с магазином через соединение.

Вы можете проверить и сравнить окончательный результат нашего проекта как тег v0.1.1 на GitHub.

В следующей части серии я более подробно расскажу о концепции промежуточного программного обеспечения redux и структуре проекта. В нашем простом и рабочем примере нам удалось заставить Redux работать всего с 30 строками кода в 2 файлах, но по мере роста нашего приложения рекомендуется разделить код Redux на отдельные файлы и импортировать только необходимые части непосредственно в наш код React. Пока что то, что мы здесь создали, лишь немного самоуверенно. Приготовьтесь к более сильным мнениям И моим причинам, стоящим за ними, в следующем эпизоде.

Немного обо мне:

Если вас заинтересовала игровая идея этого туториала, прочтите мою статью [Разбираем игры на части: как создать простой кликер в режиме ожидания]. Если вы хотите увидеть больше моей работы и прогресса, не стесняйтесь ознакомиться с моей серией о создании [Нативное веб-приложение React с крючками, колокольчиками и свистками с помощью Expo SDK33]. В настоящее время я также работаю над новой серией статей о создании более сложных и масштабируемых приложений с использованием React/Redux, где я подробно расскажу о том, как и почему я делаю то, что я делаю, а также несколько статей о моем опыте создания игры для Интернета и мобильных устройств с React. Эта статья — только начало, поэтому следите за обновлениями в будущем.

И если вы действительно чувствуете поддержку прямо сейчас, вы всегда можете поддержать меня на патреоне, что позволит мне продолжать писать уроки и предлагать поддержку в разделе комментариев.