Как разработчик React, вы могли столкнуться с Redux как с популярной библиотекой управления состоянием, которая помогает управлять состоянием вашего приложения предсказуемым и последовательным образом. Одной из ключевых концепций Redux являются редукторы, которые отвечают за изменение состояния приложения на основе действий, отправляемых компонентами.
В этом посте мы более подробно рассмотрим редукторы Redux и рассмотрим пример кода, чтобы понять, как они работают.
Редукторы в Redux
Редюсеры в Redux — это чистые функции, которые принимают текущее состояние и действие в качестве входных данных и возвращают новое состояние в зависимости от типа действия. Затем новое состояние используется для обновления глобального состояния приложения.
Redux состоит из трех основных компонентов: экшенов, редукторов и хранилища. Действия описывают изменения, которые должны произойти в состоянии приложения. Редюсеры слушают действия и соответственно обновляют состояние. Хранилище содержит состояние приложения и предоставляет интерфейс для доступа и обновления состояния.
https://ephemeral-souffle-e54f2d.netlify.app/
Я создал простой проект, чтобы продемонстрировать его в действии, проверьте проект, развернутый в netlify, чтобы почувствовать это приложение с инкрементом и декрементом.
или просто взгляните на эту гифку, ленивый разработчик.
Начнем с изучения начального состояния нашего приложения:
const match = [ { id: 1, value: 0, }, ];
Это массив с одним объектом, представляющим наше начальное состояние. Объект имеет два свойства: id
и value
. Свойство id
представляет собой идентификатор совпадения, а свойство value
представляет счет совпадения.
Затем мы создаем нашу функцию-редуктор, которая отвечает за обновление состояния в ответ на отправленные действия. Наша функция-редуктор принимает два аргумента: текущее состояние и действие. Функция редуктора структурирована как оператор switch, который обрабатывает различные типы действий:
function matchReducer(state = match, action) { if (action.type === "increment") { // update state for increment action } else if (action.type === "decrement") { // update state for decrement action } else if (action.type === "addToState") { // update state for add action } else if (action.type === "resetState") { // update state for reset action } else { // default state return state; } }
Каждый случай в операторе switch представляет другой тип действия. Когда действие отправляется, вызывается функция редуктора с текущим состоянием и действием. Затем функция редуктора возвращает новое состояние на основе действия.
Например, когда отправляется действие "increment"
, мы хотим обновить счет определенного матча. Мы делаем это, сопоставляя текущее состояние и обновляя объект соответствующим id
:
javascriptCopy code if (action.type === "increment") { return state.map((el) => { if (el.id == action.payload.id) { return { ...el, value: el.value + parseInt(action.payload.value) }; } return el; }); }
Здесь мы используем метод map
для перебора массива state
и возврата нового массива с обновленными значениями. Мы используем оператор распространения, чтобы создать копию объекта с соответствующим id
и обновить свойство value
с новой оценкой.
Точно так же, когда отправляется действие "decrement"
, мы хотим уменьшить счет определенного совпадения. Мы делаем это, находя объект с соответствующим id
, уменьшая значение свойства value
и возвращая новое состояние:
else if (action.type === "decrement") { var curState = state.find((element) => element.id == action.payload.id); const newValue = curState.value - parseInt(action.payload.value) < 0 ? 0 : curState.value - parseInt(action.payload.value); return state.map((el) => { if (el.id == action.payload.id) { return { ...el, value: newValue }; } return el; }); }
Здесь мы используем метод find
, чтобы найти объект с соответствующим id
. Затем мы вычисляем новое значение для оценки и возвращаем новое значение состояния после уменьшения.
Мы должны иметь в виду, что изменения состояния должны происходить неизменно, чтобы избыточное состояние и его предсказуемый характер сохранялись.
//create the store const store = Redux.createStore(matchReducer); //render function to update in the UI const render = () => { const state = store.getState(); // console.log(state[0]); const result = document.querySelectorAll(".lws-singleResult"); for (var i = 0; i < result.length; i++) { result[i].innerHTML = state[i].value; } }; store.subscribe(render); render();
Первая строка создает хранилище Redux с помощью функции редуктора matchReducer
. Затем определяется функция render
, которая извлекает текущее состояние из хранилища и обновляет HTML всех элементов с помощью класса lws-singleResult
. Это достигается перебором всех таких элементов и установкой их innerHTML
в соответствующее значение в состоянии.
Наконец, функция render
подписывается на хранилище с помощью store.subscribe(render)
, что означает, что она будет вызываться каждый раз, когда хранилище обновляет свое состояние. Затем сначала вызывается функция render()
, чтобы убедиться, что начальное состояние отражено в элементах HTML.
//event listeners const incrementInput = document.querySelector(".lws-increment"); const decrementInput = document.querySelector(".lws-decrement"); console.log(incrementInput); incrementInput.addEventListener("keypress", function (event) { // If the user presses the "Enter" key on the keyboard if (event.key === "Enter") { event.preventDefault(); console.log(event.target.value); store.dispatch({ type: "increment", payload: { value: event.target.value, id: 1, }, }); } }); decrementInput.addEventListener("keypress", function (event) { // If the user presses the "Enter" key on the keyboard if (event.key === "Enter") { event.preventDefault(); console.log(event.target.value); store.dispatch({ type: "decrement", payload: { value: event.target.value, id: 1, }, }); console.log(match[0].value); } }); ///Adding a new game const addMatchBtn = document.querySelector(".lws-addMatch"); addMatchBtn.addEventListener("click", function (event) { // Get all match elements var elem = document.querySelectorAll(".match"); var elemCount = document.querySelectorAll(".match").length; // Create a copy of the first match node element var clone = elem[0].cloneNode(true); // Update the ID and add a class var HeaderId = `match` + (parseInt(elemCount) + 1).toString(); clone.id = HeaderId; // Inject it into the DOM elem[elemCount - 1].after(clone); //update the match header var matchName = document.querySelector(`#${HeaderId} .lws-matchName`); matchName.innerHTML = `Match ` + (parseInt(elemCount) + 1).toString(); const incrementInput1 = document.querySelector(`#${HeaderId} .lws-increment`); const decrementInput1 = document.querySelector(`#${HeaderId} .lws-decrement`); var inputId = (parseInt(elemCount) + 1).toString(); store.dispatch({ type: "addToState", payload: { value: 0, id: inputId, }, }); incrementInput1.addEventListener("keypress", function (event) { // If the user presses the "Enter" key on the keyboard if (event.key === "Enter") { event.preventDefault(); console.log(event.target.value); store.dispatch({ type: "increment", payload: { value: event.target.value, id: inputId, }, }); } }); decrementInput1.addEventListener("keypress", function (event) { // If the user presses the "Enter" key on the keyboard if (event.key === "Enter") { event.preventDefault(); console.log(event); store.dispatch({ type: "decrement", payload: { value: event.target.value, id: inputId, }, }); } }); }); ///Resetting the whole thing const resetBtn = document.querySelector(".lws-reset"); resetBtn.addEventListener("click", function (event) { event.preventDefault(); const allInputIncrement = document.querySelectorAll(".lws-increment"); const allInputDecrement = document.querySelectorAll(".lws-decrement"); for (var i = 0; i < allInputIncrement.length; i++) { allInputIncrement[i].value = 0; allInputDecrement[i].value = 0; } store.dispatch({ type: "resetState", }); });
Этот блок кода содержит функции прослушивателя событий для различных пользовательских взаимодействий в приложении для отслеживания результатов спортивных матчей. Первые два прослушивателя событий обрабатывают нажатия клавиш на входах увеличения и уменьшения соответственно. Когда пользователь нажимает клавишу «Ввод», соответствующее входное значение отправляется в хранилище Redux с соответствующим типом действия и полезной нагрузкой.
Следующий прослушиватель событий обрабатывает добавление нового элемента соответствия, когда пользователь нажимает кнопку «Добавить совпадение». Это включает в себя клонирование первого элемента узла соответствия, обновление его идентификатора и класса, внедрение его в DOM и обновление соответствующего заголовка совпадения. Затем входные данные увеличения и уменьшения нового совпадения добавляются в хранилище Redux с типом действия «addToState» и полезной нагрузкой.
Наконец, последний прослушиватель событий обрабатывает сброс всего приложения, когда пользователь нажимает кнопку «Сброс». Это включает в себя установку всех входов инкремента и декремента на 0 и отправку действия «resetState» в хранилище Redux.
https://ephemeral-souffle-e54f2d.netlify.app/
Я создал простой проект, чтобы продемонстрировать его в действии, проверьте проект, развернутый в netlify, чтобы почувствовать это приложение с инкрементом и декрементом.
или просто взгляните на эту гифку, ленивый разработчик.
В заключение, эта необработанная реализация Redux предоставляет простой, но эффективный способ управления состоянием в веб-приложении. Хотя поначалу код может показаться пугающим, разбив его на более мелкие части и поняв роль каждого компонента, вы сможете упростить работу с ним.
В целом, Redux — это мощный инструмент для управления состоянием приложения, и эта реализация демонстрирует, как его можно использовать на практике. С помощью Redux можно создавать сложные приложения с четким разделением задач, что упрощает поддержку и отладку кода.
Я надеюсь, что эта статья помогла вам понять, как можно использовать Redux в веб-приложении. Хотя есть много других аспектов Redux, которые можно было бы изучить, таких как промежуточное ПО и асинхронные действия, эта реализация обеспечивает хорошую основу для дальнейшего развития.
Короче говоря, Redux — это ценный инструмент, который должен быть в наборе инструментов разработчика, и я призываю всех, кто интересуется веб-разработкой, изучить его подробнее. С помощью Redux можно создавать масштабируемые, удобные в сопровождении и надежные веб-приложения.