Redux vs Mobx кого же выбрать для React-приложения в 2024 году?

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, Хабр!

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

В этой статье я рассмотрю два из самых популярных и зрелых стейт менеджеров для React: Redux и Mobx. Я сравню их основные принципы, преимущества и недостатки, а также покажу примеры их использования в коде. Также я попытаюсь ответить на вопрос, какой из них лучше подходит для разработки современных приложений на React в 2024 году.

Redux

Redux — это стейт менеджер, основанный на концепции потока данных в одном направлении (unidirectional data flow). Это означает,....

А теперь простыми словами:

Redux  — это способ хранить и менять данные в приложении на React. Все данные собраны в одном месте, которое называется store.

Store — это как база данных, которая знает всю правду о приложении. Чтобы изменить что‑то в store, нужно создать и отправить action.

Action — это как сообщение, которое говорит, что нужно сделать с данными, например, добавить что‑то в список, поменять что‑то в форме, загрузить что‑то из интернета и т. д. Action не меняет данные сам, а только передает их в reducer.

Reducer — это как правило, которое говорит, как обновить store в зависимости от action. Reducer не портит старые данные, а создает новые на их основе. Все reducers собираются в один большой reducer, который обновляет store.

Чтобы компоненты React могли видеть и использовать данные из store, нужно подключить их с помощью библиотеки react‑redux. Она дает компонент Provider, который дает доступ к store всему приложению, и функцию connect, которая выбирает нужные данные из store и передает их в компоненты в виде пропсов. Также она дает возможность передавать в компоненты функции для создания и отправки actions. Когда store меняется, все подключенные компоненты обновляются с новыми данными.

Односторонний поток данных ReduxГифка из официальной документации
Односторонний поток данных Redux
Гифка из официальной документации

Вот пример кода, который демонстрирует использование Redux в приложении на React:

// actions.js
// определяем типы actions
export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO';

// определяем action creators
export function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  };
}

export function toggleTodo(index) {
  return {
    type: TOGGLE_TODO,
    index
  };
}

// reducers.js
// определяем начальное состояние store
const initialState = {
  todos: []
};

// определяем reducer для обработки actions
function todoApp(state = initialState, action) {
  switch (action.type) {
    case ADD_TODO:
      // возвращаем новое состояние с добавленным элементом в массив todos
      return {
        ...state,
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false
          }
        ]
      };
    case TOGGLE_TODO:
      // возвращаем новое состояние с переключенным флагом completed у элемента в массиве todos по индексу
      return {
        ...state,
        todos: state.todos.map((todo, index) => {
          if (index === action.index) {
            return {
              ...todo,
              completed: !todo.completed
            };
          }
          return todo;
        })
      };
    default:
      // возвращаем текущее состояние, если action не распознан
      return state;
  }
}

// index.js
// импортируем React, Redux и react-redux
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';

// импортируем наш reducer и action creators
import todoApp from './reducers';
import { addTodo, toggleTodo } from './actions';

// создаем store с помощью нашего reducer
const store = createStore(todoApp);

// определяем компонент для отображения одного элемента списка
const Todo = ({ text, completed, onClick }) => (
  <li
    style={{
      textDecoration: completed ? 'line-through' : 'none'
    }}
    onClick={onClick}
  >
    {text}
  </li>
);

// определяем компонент для отображения списка элементов
const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map((todo, index) => (
      <Todo
        key={index}
        {...todo}
        onClick={() => onTodoClick(index)}
      />
    ))}
  </ul>
);

// определяем компонент для ввода нового элемента
const AddTodo = ({ onAddClick }) => {
  let input;

  return (
    <div>
      <input ref={node => (input = node)} />
      <button
        onClick={() => {
          onAddClick(input.value);
          input.value = '';
        }}
      >
        Add Todo
      </button>
    </div>
  );
};

// определяем компонент для отображения всего приложения
const App = ({ todos, addTodo, toggleTodo }) => (
  <div>
    <AddTodo onAddClick={addTodo} />
    <TodoList todos={todos} onTodoClick={toggleTodo} />
  </div>
);

// определяем функцию, которая определяет, какие части store нужны компоненту App в виде пропсов
const mapStateToProps = state => ({
  todos: state.todos
});

// определяем функцию, которая определяет, какие action creators нужны компоненту App в виде пропсов
const mapDispatchToProps = dispatch => ({
  addTodo: text => dispatch(addTodo(text)),
  toggleTodo: index => dispatch(toggleTodo(index))
});

// подключаем компонент App к store с помощью функции connect
const ConnectedApp = connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

// рендерим компонент Provider, который передает store в контекст, и компонент ConnectedApp внутри него
ReactDOM.render(
  <Provider store={store}>
    <ConnectedApp />
  </Provider>,
  document.getElementById('root')
);

Что хорошего в Redux?

  • Redux обеспечивает прозрачность и предсказуемость потока данных, так как все изменения состояния происходят только через actions и reducers, которые являются чистыми функциями, не зависящими от внешних факторов. Это упрощает отладку, тестирование и отслеживание изменений состояния, а также позволяет использовать различные инструменты и расширения, которые улучшают разработку и документирование кода.

    Например: Redux DevTools — это расширение для браузера, которое позволяет просматривать историю actions и состояния, а также перематывать их во времени, изменять состояние и actions, и многое другое.

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

Что плохого в Redux?

  • Redux имеет высокий порог вхождения и сложную кривую обучения, так как он требует знания и понимания многих концепций, терминов, паттернов и библиотек, которые не всегда интуитивны и легко запоминаются.

    Например: Для создания простого приложения на Redux нужно определить actions, action creators, reducers, store, Provider, connect, mapStateToProps, mapDispatchToProps и т. д.

  • Redux приводит к большому количеству кода и бойлерплейта, так как он требует написания многочисленных функций, объектов, констант, импортов и экспортов, которые зачастую повторяются и не несут смысловой нагрузки.

    Например: для добавления нового action нужно определить его тип, action creator, обработчик в reducer, импортировать и экспортировать их, а также передать action creator в mapDispatchToProps и вызвать его из компонента.

  • Redux может приводить к проблемам производительности и избыточным перерисовкам, так как он обновляет store при каждом action, и перерисовывает все компоненты, которые подписаны на него, даже если их данные не изменились. Для решения этой проблемы нужно использовать дополнительные техники и библиотеки, такие как reselect, memo, useMemo, useCallback и т. д.

Mobx

Mobx — это стейт менеджер, основанный на концепции реактивного программирования (reactive programming).

Это означает, что данные в Mobx хранятся в специальных объектах, называемых observables, которые автоматически отслеживают и оповещают об изменениях своих значений. Для изменения данных в observables используются обычные операции присваивания, добавления, удаления и т. д.

Для связи компонентов React с observables используется библиотека mobx‑react, которая предоставляет декоратор @observer, который оборачивает компоненты в специальные функции, называемые reactions, которые автоматически подписываются на observables, которые используются в рендере компонента, и перерисовывают компонент, когда они изменяются.

Также mobx‑react предоставляет компонент Provider, который позволяет передавать observables в контекст, и функцию inject, которая позволяет получать observables из контекста в виде пропсов.

by Nethmee Kumararatne
by Nethmee Kumararatne

Вот пример кода, который демонстрирует использование Mobx в приложении на React:

// store.js
// импортируем Mobx
import { observable, action } from 'mobx';

// определяем класс для хранения данных
class TodoStore {
  // определяем массив для хранения элементов списка
  @observable todos = [];

  // определяем действие для добавления нового элемента в список
  @action
  addTodo = text => {
    this.todos.push({
      text,
      completed: false
    });
  };

  // определяем действие для переключения флага completed у элемента в списке по индексу
  @action
  toggleTodo = index => {
    this.todos[index].completed = !this.todos[index].completed;
  };
}

// создаем экземпляр класса и экспортируем его
const store = new TodoStore();
export default store;

// index.js
// импортируем React и mobx-react
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider, observer, inject } from 'mobx-react';

// импортируем наш store
import store from './store';

// определяем компонент для отображения одного элемента списка
const Todo = ({ text, completed, onClick }) => (
  <li
    style={{
      textDecoration: completed ? 'line-through' : 'none'
    }}
    onClick={onClick}
  >
    {text}
  </li>
);

// определяем компонент для отображения списка элементов
const TodoList = observer(({ todos, onTodoClick }) => (
  <ul>
    {todos.map((todo, index) => (
      <Todo
        key={index}
        {...todo}
        onClick={() => onTodoClick(index)}
      />
    ))}
  </ul>
));

// определяем компонент для ввода нового элемента
const AddTodo = ({ onAddClick }) => {
  let input;

  return (
    <div>
      <input ref={node => (input = node)} />
      <button
        onClick={() => {
          onAddClick(input.value);
          input.value = '';
        }}
      >
        Add Todo
      </button>
    </div>
  );
};

// определяем компонент для отображения всего приложения
const App = ({ store }) => (
  <div>
    <AddTodo onAddClick={store.addTodo} />
    <TodoList todos={store.todos} onTodoClick={store.toggleTodo} />
  </div>
);

// оборачиваем компонент App в функцию inject, которая передает store в виде пропса
const ConnectedApp = inject('store')(App);

// рендерим компонент Provider, который передает store в контекст, и компонент ConnectedApp внутри него
ReactDOM.render(
  <Provider store={store}>
    <ConnectedApp />
  </Provider>,
  document.getElementById('root')
);

Что хорошего в Mobx?

  • Mobx обеспечивает простоту и удобство потока данных, так как он не требует создания и обработки actions и reducers, а позволяет изменять данные напрямую с помощью обычных операций, а также автоматически подписывает и перерисовывает компоненты, которые используют observables.
    Это снижает количество кода и бойлерплейта, а также упрощает понимание и отслеживание изменений состояния.

  • Mobx позволяет адаптировать приложения, так как он не навязывает строгую архитектуру и паттерны, а предоставляет гибкость и свободу в выборе структуры и организации данных.
    Это позволяет использовать Mobx в разных сценариях и совмещать его с другими библиотеками и решениями.

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

Что плохого в Mobx?

  • Mobx может приводить к непонятности и неочевидности потока данных, так как он скрывает многие детали и механизмы работы observables, reactions и actions, а также не требует явного определения источников и потребителей данных.
    Это может затруднять отладку, тестирование и отслеживание изменений состояния, а также приводить к ошибкам и неожиданному поведению приложения.

  • Mobx может приводить к зависимости и несовместимости приложений, так как он использует многие специфичные и экспериментальные возможности JavaScript, такие как декораторы, прокси, генераторы и т. д., которые не поддерживаются всеми браузерами и средами.
    Это требует использования дополнительных инструментов и конфигураций, таких как Babel, Webpack, TypeScript и т. д., которые могут усложнять и замедлять процесс разработки и сборки приложений.

Вывод

Redux и Mobx — это два разных подхода к управлению состоянием и данными в приложениях на React, которые имеют свои преимущества и недостатки.

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

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

Выбор между Redux и Mobx зависит от многих факторов, таких как размер, сложность, цель и специфика приложения, а также предпочтения, опыт и навыки разработчиков. Нет однозначного ответа на вопрос, какой из них лучше подходит для разработки современных приложений на React в 2024 году.

Возможно, что в некоторых случаях лучше использовать их вместе (следите за блогом, скоро напишу про это статью), а в некоторых — вообще обойтись без них, используя встроенные средства React, такие как useState, useReducer, useContext и т. д. Главное — понимать принципы и особенности каждого из них, и выбирать тот, который наиболее соответствует потребностям и задачам конкретного проекта.

Статьи

Если вы все же не определились между Redux и MobX, я предложу вам эти статьи:

  • MobX vs Redux — What to Choose in 2024? — Aglowid IT Solutions — эта статья дает подробное сравнение между MobX и Redux, а также дает таблицу с основными аспектами их работы.

  • Should You Still Go with Redux in 2024? Exploring Alternatives and Comparisons — Medium — эта статья рассматривает не только MobX и Redux, но и другие альтернативы, такие как React Context API, Recoil и Zustand, а также дает рекомендации по их выбору.

  • reactjs — Difference between: MobX and Redux — Stack Overflow — это ответ на вопрос на популярном сайте для разработчиков, который дает краткий обзор преимуществ и недостатков MobX и Redux.

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

Спасибо за внимание!

Источник: https://habr.com/ru/articles/795901/


Интересные статьи

Интересные статьи

Python является одним из самых популярных языков программирования в мире. Он широко используется в различных сферах, таких как веб-разработка, научные исследования, анализ данных, машинное обучение и ...
В 2024 году Rust остается одним из наиболее востребованных и перспективных языков программирования. Вот несколько причин, почему учить Rust может быть полезно:1. Безопасность: Rust разработан с упором...
В начале января 2024 года Microsoft напомнила, что поддержка фирменной среды программирования Visual Studio 2013 заканчивается 9 апреля 2024 года. Это означает, что десятилетняя версия IDE от Microsof...
Redux и Effector — это две популярные библиотеки, которые используются для  управления состоянием в веб-приложениях. Но если первая широко известна во frontend-разработке, вторая только набирает ...
- Папа, это красная смородина? - Нет, черная.- А почему она белая?- Потому что еще зеленая.Начнем с простого. Цветная металлургия включает в себя все металлы и сплав...