Станислав Быков

Frontend разработчик в ГК Юзтех

Сегодня я хотел бы рассказать о Rematch — библиотеке, которая предоставляет удобный и эффективный способ управления состоянием ваших веб-приложений. Если вы уже знакомы с Redux и ищете более простое и компактное решение, то Rematch может оказаться для вас полезным инструментом. 

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

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

С помощью Rematch вы можете определить модели (models) и их состояние, эффекты (effects) и редьюсеры (reducers). Он предоставляет удобные инструменты для работы с асинхронными операциями, а также возможность создания селекторов (selectors) для выборки данных из хранилища. Все это помогает организовать логику вашего приложения и управлять его состоянием с минимальными усилиями.

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

Введение в Rematch

Rematch — это интуитивный и легковесный фреймворк для управления состоянием вашего веб-приложения на основе Redux. Он предлагает простой и эффективный подход к организации и управлению Redux-хранилищем, сокращая количество лишнего кода и упрощая разработку.

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

В процессе работы с библиотекой я отметил следующие ее полезные особенности: 

  • маленький размер (меньше 2Кб);

  • отсутствие необходимости конфигурации;

  • встроенная поддержка сайд-эффектов;

  • поддержка Redux Devtools, TypeScript;

  • поддержка динамического добавления редьюсеров, горячей перезагрузки;

  • возможность создания нескольких хранилищ;

  • расширяемость функционала с помощью плагинов; 

  • возможность использования в React Native.

Если сравнивать Rematch с прямым использованием Redux, его основные преимущества:

  • Простота и понятный синтаксис. Rematch предлагает простую и интуитивно понятную модель, которая позволяет определить модели состояния, эффекты и редьюсеры с минимальным количеством кода. Он освобождает разработчиков от необходимости писать многочисленные действия (actions), константы и редьюсеры, что делает код более лаконичным и понятным.

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

  • Мощные эффекты. Rematch предоставляет простой способ определения и обработки асинхронных операций и сайд-эффектов с помощью эффектов. Он интегрируется с middleware Redux, что позволяет легко управлять асинхронными запросами, обращениями к API и другими сайд-эффектами.

  • Расширяемость. Rematch позволяет легко расширять функциональность с помощью плагинов. Вы можете использовать готовые плагины или создать свои собственные, чтобы добавить новые возможности и интеграции в свое приложение.

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

Основные концепции Rematch

Здесь я покажу основные концепции Rematch, которые помогут вам организовать и управлять состоянием вашего приложения.

Модели (Models)

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

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

/* ./models/count.ts */

import { createModel } from "@rematch/core";
import { RootModel } from ".";

export const count = createModel<RootModel>()({
  state: 0, // начальное состояние
  reducers: {
    increment(state, payload: number) {
      return state + payload;
    },
    decrement(state, payload: number) {
      return state - payload;
    },
  },
  effects: (dispatch) => ({
    async incrementAsync(payload: number, state) {
      await new Promise((resolve) => setTimeout(resolve, 1000));
      dispatch.count.increment(payload);
    },
});

export default counter;
import { Models } from "@rematch/core";
import { count } from "./count";
import { example } from "./example";

export interface RootModel extends Models<RootModel> {
  count: typeof count;
  example: typeof example;
}

export const models: RootModel = { count, example };

Хранилище (Store)

Хранилище представляет собой объединение нескольких моделей в одну структуру данных. Оно является единственным источником правды для вашего приложения и хранит состояние всех моделей.

Для создания хранилища Rematch использует функцию init, которая принимает объект с моделями:

import { init, RematchDispatch, RematchRootState } from "@rematch/core";
import { models, RootModel } from "./models";

export const store = init({
  models,
});

export type Store = typeof store;
export type Dispatch = RematchDispatch<RootModel>;
export type RootState = RematchRootState<RootModel>;

Диспатч экшенов (Dispatch actions)

С помощью диспатча вы можете вызывать редьюсеры и эффекты ваших моделей. Диспатч можно вызывать привычным способом, как в Redux, или короткой записью:

const { dispatch } = store;

dispatch({ type: "count/increment", payload: 1 });
dispatch.count.increment(1);

dispatch({ type: "count/incrementAsync", payload: 1 });
dispatch.count.incrementAsync(1);

Доступ к состоянию и действиям

Чтобы получить доступ к состоянию и действиям моделей в компонентах вашего приложения, вы можете использовать хук useSelector и метод dispatch:

import * as React from "react";
import { useSelector, useDispatch } from 'react-redux';
import { RootState, Dispatch } from './store';

const CounterComponent = () => {
  const count = useSelector((state: RootState) => state.count);
  const dispatch = useDispatch<Dispatch>();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => dispatch.count.increment(1))}>Increment</button>
      <button onClick={() => dispatch.count.decrement(1))}>Decrement</button>
    </div>
  );
};

export default CounterComponent;

В этом примере мы используем хук useSelector для получения значения состояния "counter" из хранилища, а также метод dispatch для вызова соответствующих редьюсеров.

Эффекты (Effects)

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

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

const counter = createModel<RootModel>()({
  state: 0,
  reducers: {
    // ...
  },
  effects: {
    async fetchData(payload, rootState) {
      try {
        const response = await fetch(`http://example.com/${payload}`);
        const data = await response.json();
        // Действия с полученными данными
      } catch (error) {
        // Обработка ошибок
      }
    },
  },
});

Плагины (Plugins)

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

Существует множество плагинов, доступных для использования с Rematch. Некоторые из них были написаны непосредственно командой авторов этой библиотеки, но также есть такие, которые создавались сообществом, на npm/github, и никто не запрещает написать свой собственный плагин.

Ниже вы найдете краткое описание плагинов, созданных командой Rematch:

  • @rematch/immer: Обертывает ваши редьюсеры с помощью библиотеки immer, что позволяет безопасно выполнять изменения состояния с помощью мутаций, при этом сохраняя неизменяемость состояния.

  • @rematch/select: Добавляет возможность использовать селекторы (selectors) для выбора данных из состояния. Селекторы создаются с использованием библиотеки reselect по умолчанию и автоматически связываются с зависимостями селекторов из других моделей.

  • @rematch/loading: Плагин для отображения индикаторов загрузки при выполнении эффектов. Помогает избежать ручного управления состоянием загрузки, добавляя автоматическую поддержку загрузки в Rematch.

  • @rematch/updated: Плагин, позволяющий поддерживать временные метки (timestamps) при вызове эффектов. Он применяется преимущественно для оптимизации эффектов и может использоваться для предотвращения выполнения дорогостоящих запросов в течение определенного периода времени или для ограничения частоты выполнения эффектов.

  • @rematch/persist: Предоставляет автоматическую персистентность состояния Redux. Это позволяет сохранять состояние между перезагрузками страницы или после закрытия и повторного открытия приложения.

  • @rematch/typed-state: Предназначен для проверки типов состояния во время выполнения. Использует prop-types для описания ожидаемой формы типов.

Итог

В этой статье я рассказал о Rematch — мощной библиотеке для управления состоянием приложений. На основании своего опыта выделил основные преимущества Rematch и на практике показал, как легко и эффективно использовать его для организации Redux-хранилища и управления состоянием.

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

Благодаря поддержке множества плагинов, Rematch может быть легко расширен и адаптирован под ваши конкретные потребности. Мы рассмотрели некоторые популярные плагины, такие как плагин для сохранения состояния с помощью redux-persist, плагин для использования immer.js и плагин для создания селекторов с помощью reselect.

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

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

Если у вас есть опыт использования Rematch, поделитесь им в комментариях.

Комментарии (14)


  1. olegkusov
    14.09.2023 20:59
    +1

    В чем его преимущества перед zustand? Пока вижу лишь многословность


    1. egorchh
      14.09.2023 20:59

      Статья ведь не об этом, а о преимуществах перед нативным Redux


  1. markelov69
    14.09.2023 20:59

    В чем его преимущества перед MobX? Спойлер их нет и быть не может. Ибо:

    import { observer } from "mobx-react-lite";
    import { makeAutoObservable } from "mobx";
    
    class State {
        firstName = '';
        counter = 1;
        isFetching = false;
        serverData = null;
        serverError = null;
    
        constructor() {
            makeAutoObservable(this);
        }
    
        incr = () => {
          this.counter++;
        }
    
        handleFirstNameChange = (e) => {
          this.firstName = e.target.value;
        }
    
        fetchData = async () => {
            this.isFetching = true;
            try {
              this.serverData = await someApiRequest();
              this.serverError = null;
            } catch (e) {
              this.serverError = e;
            } finally {
              this.isFetching = false;
            }
        };
    }
    
    const state = new State();
    
    const App = observer(() => {
        useEffect(() => { state.fetchData() }, []);
    
        function decr() {
          state.counter--;    
        }
      
        return (
            <div>
              <input value={state.firstName} onChange={state.handleFirstNameChange} />
              <div>counter: {state.counter}</div>
              <button onClick={state.incr}>incr</button>
              <button onClick={decr}>decr</button>
              <div>server data: {state.serverData}</div>
              <div>server fetching: {state.isFetching.toString()}</div>
            </div>
        )
    });

    Вот посмотреть более расширенный пример и поиграться:
    https://codesandbox.io/s/adoring-banach-k8m9ss?file=/src/App.tsx

    В этой статье я рассказал о Rematch — мощной библиотеке для управления состоянием приложений. 

    Мощной? Это шутка такая что ли?) Это типичный банальный ручной pub/sub как и redux и т.п.

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

    Серьезно? Посмотрите на MobX и вы вообще тогда будете в культурном шоке. Вот где реальное отсутствие бойлерплейта и минимально возможное кол-во кода, ну и разумеется реально удобный синтаксис(т.к. он просто нативный).

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

    Статья - очередное подтверждение что стейт менеджмент не основанный на getters/setters это просто очередная полнейшая шляпа. И уж тем более использовать такие стейт менеджеры в своих проектах это максимально абсурдно и нелепо.


    1. UbuRus
      14.09.2023 20:59
      +1

      Вот так вот одна технология во всем лучше, чем другая? Нашли ту самую серебряную пулю стейтменеджмента?


      1. markelov69
        14.09.2023 20:59

        Вот так вот одна технология во всем лучше, чем другая? Нашли ту самую серебряную пулю стейтменеджмента?

        С удовольствуем буду использовать технологию которая будет ещё лучше, только вопрос, где она?


        1. nin-jin
          14.09.2023 20:59

          Тут: https://mol.hyoo.ru/#!section=docs/=yvd6t1_jkrmkc

          Лучше по всем параметрам.


        1. UbuRus
          14.09.2023 20:59

          Как обычно: it depends.
          Для размышления: в микросервисах есть множество паттернов коммуникации, от синхронных вызовов, до event sourcing. И нет одного "лучшего" способа. Так и тут, где-то будет удобнее event sourcing (redux, etc) – offline/local first приложения, где-то mobx, где-то zustand или вообще достаточно встроенного useSyncExternalStore и наколеночный стор.


          1. markelov69
            14.09.2023 20:59

            Как обычно: it depends.

            Конкретно во front-end приложениях, управление состоянием есть управление состоянием. И вариантов и кейсов где redux, rematch, effector, zustand и т.п. будет удобнее применять чем mobx(или самописный аналог на getters/setters) просто нет, проверено годами. Ибо ты просто тупо изменил переменную и автоматически приложение на это отргеариговало - всё, в этом и заключается вся суть и весь смысл.


    1. egorchh
      14.09.2023 20:59
      +1

      У MobX, как минимум, огромный вес пакета.


      1. Alexandroppolus
        14.09.2023 20:59
        +1

        Я как-то сравнивал суммарный размер "mobx" + "mobx-react-lite" против Редукса и его дополнительного обвеса. Получилось не сказать что сильно больше.


        1. egorchh
          14.09.2023 20:59

          Не знал о таком конфиге, спасибо


    1. nin-jin
      14.09.2023 20:59

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


  1. egorchh
    14.09.2023 20:59

    Отмечу, что Rematch постоянно развивается и улучшается

    Судя по их гитхабу, последний коммит был почти год назад


  1. nin-jin
    14.09.2023 20:59

    А чем оно отличается о RTK?