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

9192f18fedb55259fb4bcec83e3ee31e.jpg

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

Сегодня я хочу поделиться с вами своими размышлениями о том, какой стейт менеджер лучше использовать для разработки приложений на 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 }) => (
  
  • {text}
  • ); // определяем компонент для отображения списка элементов const TodoList = ({ todos, onTodoClick }) => (
      {todos.map((todo, index) => ( onTodoClick(index)} /> ))}
    ); // определяем компонент для ввода нового элемента const AddTodo = ({ onAddClick }) => { let input; return (
    (input = node)} />
    ); }; // определяем компонент для отображения всего приложения const App = ({ todos, addTodo, toggleTodo }) => (
    ); // определяем функцию, которая определяет, какие части 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( , 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

    Вот пример кода, который демонстрирует использование 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 }) => (
      
  • {text}
  • ); // определяем компонент для отображения списка элементов const TodoList = observer(({ todos, onTodoClick }) => (
      {todos.map((todo, index) => ( onTodoClick(index)} /> ))}
    )); // определяем компонент для ввода нового элемента const AddTodo = ({ onAddClick }) => { let input; return (
    (input = node)} />
    ); }; // определяем компонент для отображения всего приложения const App = ({ store }) => (
    ); // оборачиваем компонент App в функцию inject, которая передает store в виде пропса const ConnectedApp = inject('store')(App); // рендерим компонент Provider, который передает store в контекст, и компонент ConnectedApp внутри него ReactDOM.render( , 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, я предложу вам эти статьи:

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

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

    © Habrahabr.ru