Как разработчик 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 можно создавать масштабируемые, удобные в сопровождении и надежные веб-приложения.