На прошлой неделе впервые поучаствовал в конференции по Frontend, где один из докладчиков, расказывал, как удачно его команда переехала с Redux на Mobx. Главным преимуществом он назвал отсутствие бойлерплейта и ускорение разработки в полтора раза.

Я прочитал несколько статей и посмотрел другие доклады, где все как один говорят, что Mobx лучше, чем Redux. Возможно это и так, но почему в сравнение всегда идет Redux, а не Redux-Toolkit, я не понимаю. Попытаемся конструктивно посмотреть действительно ли Mobx настолько хорош как о нем говорят.

Главный аргумент адептов Mobx звучит примерно так

При разработке на Redux приходится писать тону шаблонного кода, чтобы все работало. Нужно написать action-ы и selectors-ы.

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

import { makeAutoObservable } from "mobx";
import { IPromiseBasedObservable, fromPromise } from "mobx-utils";

/* Типизация */
const PostListSchema = z.object({
  id: z.number(),
  title: z.string(),
  description: z.string(),
  tag: z.string(),
  tags: z.array(z.string()),
  image: z.string(),
  progress: z.number(),
  progressTotal: z.number()
})

type PostListModel = z.infer<typeof PostListSchema>

/* Запрос на получение данных */
export const fetchPostList = async (limit: number) => {
  try {
    const response = await _api.get<PostListModel[]>(`api/posts`)

    if (!response.data) {
      throw new Error("Ошибка")
    }

    return response.data.data
  } catch {
    throw new Error("Ошибка")
  }
}


/* Создание стора */
class PostListStore {
    posts?: IPromiseBasedObservable<PostListModel[]>
    counter: 0

    constructor() {
        makeAutoObservable(this)
    }

    incrementCounter = () => {
      this.counter += 1
    }

    decrementCounter = () => {
      this.counter -= 1
    }

    fetchCoursesData = (limit: number) => {
        this.courses = fromPromise(fetchPostList(limit))
    }
}

export const postListStore = new PostListStore()

Теперь попробуем написать такую же логику на Redux-Toolkit. Но чтобы избежать предвзятости в нашей оценке давайте попросим chatGPT написать код за нас.

import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { AppThunk } from "./store";

/* Типизация */
interface PostListState {
  posts: PostListModel[] | null;
  loading: boolean;
  error: string | null;
  counter: number;
}

const initialState: PostListState = {
  posts: null,
  loading: false,
  error: null,
  counter: 0,
};

/* Создание слайса */
const postListSlice = createSlice({
  name: "postList",
  initialState,
  reducers: {
    incrementCounter(state) {
      state.counter += 1;
    },
    decrementCounter(state) {
      state.counter -= 1;
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchPostListAsync.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchPostListAsync.fulfilled, (state, action: PayloadAction<PostListState[]>) => {
        state.loading = false;
        state.posts = action.payload;
      })
      .addCase(fetchPostListAsync.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message ?? "Ошибка";
      });
  },
});

export const { incrementCounter, decrementCounter } = postListSlice.actions;

export default postListSlice.reducer;

/* Запрос на получение данных */
export const fetchPostListAsync = createAsyncThunk("fetchPostList", async () => {
  try {
    const response = await axios.get("/api/posts")

    if (!response.data) {
      throw new Error("Ошибка")
    }

    return response.data
  } catch {
    throw new Error("Ошибка")
  }
})

Реализация кода очень похожа, единственное, что в mobx это выглядит немного проще. Однако в сумме разница в 10 строчек, не могу назвать это бойлерплейтом. Actions писать тоже не нужно toolkit все делает за нас.

В рамках эксперимента, давайте попросим chatGPT написать компонент PostList с использованием Mobx и Redux-Toolkit.

/* Код с использованием Mobx */

import React, { useEffect } from "react";
import { observer } from "mobx-react-lite";
import { postListStore } from "../stores/postListStore";
import { IPromiseBasedObservableState } from "mobx-utils";

const PostListMobX: React.FC = observer(() => {
  useEffect(() => {
    postListStore.fetchCoursesData(10); // Загружаем посты при монтировании компонента
  }, []);

  const { state } = postListStore.posts ?? {};

  switch (state) {
    case "pending":
      return <div>Loading...</div>;
    case "rejected":
      return <div>Error: Failed to fetch posts</div>;
    case "fulfilled":
      return (
        <div>
          {postListStore.posts?.value.map((post) => (
            <div key={post.id}>
              <h2>{post.title}</h2>
              <p>{post.description}</p>
              {/* Другие поля поста */}
            </div>
          ))}
        </div>
      );
    default:
      return null;
  }
});

export default PostListMobX;

Возможно стоит отметить, что с кодом для Mobx у GPT возникли трудности и правильный результат удалось получить только с четвертой попытки.

/* Код с использованием Redux-toolkit */

import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "../store";
import { fetchPostListAsync } from "../postListSlice";

const PostListRedux: React.FC = () => {
  const dispatch = useDispatch();
  const { posts, loading, error } = useSelector((state: RootState) => state.postList);

  useEffect(() => {
    dispatch(fetchPostListAsync(10)); // Загружаем посты при монтировании компонента
  }, [dispatch]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!posts) return null;

  return (
    <div>
      {posts.map((post) => (
        <div key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.description}</p>
          {/* Другие поля поста */}
        </div>
      ))}
    </div>
  );
};

export default PostListRedux;

Опять же результат примерно одинаковый в плане количества кода. Однако решение с использованием Redux-Toolkit смотрится проще.

На мой взгляд само сравнение не Toolkit версии с Mobx, крайне странно. Я думаю, это сравнение имело актуальность в 2020 году может быть, но в 2024 точно нет. Для себя я все таки сделаю вывод, что оба инструмента не заставляют разработчика писать "тону" шаблонного кода.

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


  1. mayorovp
    19.04.2024 18:10
    +3

    Только вот на таком простом примере для mobx вообще не требуется отдельный стор. А при усложнении примеров совсем не факт, что оба варианта будут усложняться одннаково.

    Кстати, а почему вы в примере с mobx реализовали fetchPostList, а в примере с redux импортировали его из какого-то.api?


    1. artemmorozov13 Автор
      19.04.2024 18:10

      Извиняюсь, да нужно было брать запрос котрый написан ниже fetchPostListAsync и типизация через описаный тип выше, поправил


      1. mayorovp
        19.04.2024 18:10

        И откуда у обычной функции fetchPostListAsync появились дополнительные свойства? Надеюсь, прототипы функций эта библиотека не ковыряет?


  1. gen1lee
    19.04.2024 18:10

    В redux можно вообще без блоилерплейта и без redux-toolkit. Там можно как угодно, и все это работает без классов и без магии, без всяких Observable и high order component, в функциональном стиле. Максимально простая библиотека, которую можно написать за один вечер самому. Потому и популярнее чем MobX.


    1. artemmorozov13 Автор
      19.04.2024 18:10
      +1

      Согласен, да. Очень часто слышу от людей, что redux г*овно т.к. много шаблонного кода, решил посмотреть, так ли это


      1. gen1lee
        19.04.2024 18:10

        Ну вы можете лицезреть здесь уровень тех, кто так говорит - сплошная некомпетентность, незнание основ redux и react, ни на один мой комментарий нет конструктивной критики или диалога, только минусы.


  1. ivankoltsov
    19.04.2024 18:10

    На простом примере для MobX отдельный стор не нужен.


    1. artemmorozov13 Автор
      19.04.2024 18:10

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


  1. Modin
    19.04.2024 18:10

    Так, круто, с бойлерплейтом разобрались. Теперь давайте разберемся с оптимизацией. Что предлагает редакс и mobx. Чтобы было проще предлагаю посмотреть, зачем нужен observer и причем тут proxy или get/set


    1. Modin
      19.04.2024 18:10

      В дополнение, предлагаю вам рассказать, что может предложить Mobx и redux в части локальных стейтов, когда не нужна «глобальная область видимости», а нужен конкретный стор для конкретного компонента, который будет работать как useState, но не наследовать его недостатков.


      1. artemmorozov13 Автор
        19.04.2024 18:10

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


        1. Modin
          19.04.2024 18:10

          Конечно. У вас есть список пользователей, при клике на одном из них вы переходите к странице с личной информацией, на которой у вас есть «жирный клиент» с возможностью добавлять, удалять и редактировать поля. Хранить такой стор как глобальный плохая практика, ведь при изменении юзера вам нужно делать условный «reset” стора, чтобы данные от прошлого юзера были затерты


          1. gen1lee
            19.04.2024 18:10

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


            1. Modin
              19.04.2024 18:10

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


              1. artemmorozov13 Автор
                19.04.2024 18:10
                +1

                Я думаю он имел ввиду щапрашивать профиль отдельно и добавлять его через entityAdapter, а если он уже запрашивался, то доставать по id.
                Я думаю что оба варианта реализации хороши


              1. gen1lee
                19.04.2024 18:10

                Я нигде не писал про то, как они получаются. Они получатся могут как списком, с экрана со списком, и это абсолютно нормально, так и по отдельности.


            1. mayorovp
              19.04.2024 18:10

              И получаем утечку памяти при длительном использовании приложения?


              1. gen1lee
                19.04.2024 18:10

                "Утечку памяти" в пару килобайт? При желании всегда можно удалять объекты, которые не нужны, но это чистая преждевременная оптимизация в большинстве случаев, так как они практически ничего не занимают. Так еще и при повторном открытии экрана не будет показан спинер на весь экран.


                1. mayorovp
                  19.04.2024 18:10

                  Да пусть и в пару килобайт. Некоторые пользователи годами вкладки не закрывают...


                  1. artemmorozov13 Автор
                    19.04.2024 18:10

                    Сомневаюсь, что refresh token у кого то столько живет


                    1. mayorovp
                      19.04.2024 18:10

                      А утёкшая память что, освобождается при повторном логине?


                      1. gen1lee
                        19.04.2024 18:10

                        Грамотный логаут мало того, что очищает стор, так по хорошему еще и перезагружает страницу.


          1. artemmorozov13 Автор
            19.04.2024 18:10

            Спасибо


      1. gen1lee
        19.04.2024 18:10

        Городить костыли вместо useState для локального состояния это очень сомнительная идея.


        1. Modin
          19.04.2024 18:10
          +2

          Городить спред операторы и безумную логику без оптимизации - вот это сомнительная идея. По факту, useState хорош либо когда мы «получили данные и забыли», либо когда нам нужен boolean | string | number в useState.


          1. gen1lee
            19.04.2024 18:10

            Для более сложного состояния придумали useReducer. Почитайте документацию чем городить костыли.


            1. Modin
              19.04.2024 18:10

              Как useReducer поможет при изменении ссылки в памяти при перерендеринге?


              1. gen1lee
                19.04.2024 18:10

                Для ссылок придумали useRef. Почитайте доку. Хотя что имеется ввиду вообще не ясно.


      1. gun_dose
        19.04.2024 18:10
        +1

        А можно подробнее про недостатки useState? Я вообще противник того, чтобы класть в redux то, что никогда не пригодится за пределами конкретного компонента. И описанную вами ниже задачу я бы решал именно через useState.

        Почему я против того, чтобы хранить такое в redux? У меня есть очень большой проект на next.js и там больше десятка разных типов страниц и стэйты всех страниц засунуты в redux, в итоге при загрузке страницы next data весит порядка 2МБ, но большая часть глобального стейта - это дефолтные значения стейтов других страниц. Плюс запрос getServerSideProps занимает очень много времени (попядка 2 секунд), т.к. вместо того, чтобы запросить json данные с бэка и передать их на фронт, каждый раз на сервере происходит инициализация redux, запуск всего его жизненного цикла, потом сериализация стейта и отправка этого клиенту. Причём опять же большая часть респонса не нужна на запрашиваемой странице.


      1. acsent1
        19.04.2024 18:10

        Локальный стор - это же context. Естественно, если компонента состоит не нескольких сабкомпонент. Иначе вообще стор не нужен


        1. Modin
          19.04.2024 18:10
          +1

          -> Локальный стор - это же context. 

          Store / state и тд это хранилище, контекст тут вообще не при чем


    1. artemmorozov13 Автор
      19.04.2024 18:10

      Обязательно напишу об оптимизации, пока что решил посмотреть на главный аргумент засирания Redux, комюнити mobx


      1. Modin
        19.04.2024 18:10
        +1

        Это один из главных аргументов. Но это дело вкусовщины. Нравится вам бойлерплейт и идеология flux, нет проблем. Проблема в том, что это не избавляет вас от «проблем» реакта, таких, как например, always new свойств. С Mobx useCallback, useMemo, memo, можно позабыть, потому что он решает проблемы связанные с ними


        1. artemmorozov13 Автор
          19.04.2024 18:10
          +1

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

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


          1. Modin
            19.04.2024 18:10

            В редаксе и прочих библиотеках где не производится deep comparsion, не обойтись без встроенных инструментов реакта. Или смириться с неизбежным перерендерингом компонентов


            1. gen1lee
              19.04.2024 18:10

              deep comparison это признак плохого кода и плохой оптимизации, и в react и redux он как раз не нужен из за грамотной архитектуры. Встроенные инструменты реакта вообще нужны для самого реакта в первую очередь, редакс обходится reselect если вообще нужно.


              1. Modin
                19.04.2024 18:10

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


                1. gen1lee
                  19.04.2024 18:10

                  Именно на таких клиентах никому не придет в голову в здравом уме делать deep comparison когда можно обойтись shallow, и именно такими проектами я и занимался/занимаюсь. Так что не вам мне рассказывать.


                  1. mayorovp
                    19.04.2024 18:10

                    А причём тут вообще deep comparison?


            1. artemmorozov13 Автор
              19.04.2024 18:10

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


    1. healthxp1
      19.04.2024 18:10

      Да, полностью плюсую комментарию, что не в одном бойлерплейте дело: из-за того, что в Redux мы работаем с единым большим стором с определенного момента начинаются неизбежные проблемы с оптимизацией приложения (чего нет в MobX из-за разницы в реализации библиотек)

      И добавлю, что в целом при работе с Redux могут быть различные подходы (например, асинхронщина может реализовываться как через Redux Thunk / Sagas / RxJS + Redux Observable - и в зависимости от выбранного подхода количество кода также будет значительно различаться.


      1. artemmorozov13 Автор
        19.04.2024 18:10
        +1

        В чем проблема с оптимизацией в Redux при разростании приложения? Не вижу проблем


        1. Alexandroppolus
          19.04.2024 18:10

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


          1. gen1lee
            19.04.2024 18:10

            Нет такой проблемы в redux, у вас очень слабое знание этой библиотеки.


            1. gen1lee
              19.04.2024 18:10

              Поясню для любителя минусовать - с нормализацией, если список подписан на список из id, а конкретный пост на объект c id, то список не перерендерится. Но, минусаторы на то и минусаторы, что им все равно.


          1. artemmorozov13 Автор
            19.04.2024 18:10

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


            1. Alexandroppolus
              19.04.2024 18:10
              +1

              Да, нормализация данных, я уж и подзабыл) Но тогда это в копилку лишнего бойлерплейта, разве нет? Мы вынуждены нормализовывать, а в мобх можно обойтись просто массивом объектов.


              1. artemmorozov13 Автор
                19.04.2024 18:10

                Ну, как вы будете изменять элемент, если передали только id? Нужно будет нормализовывать


                1. mayorovp
                  19.04.2024 18:10

                  Очевидно, что без нормализации передавать нужно не id, а сам объект. В чём вы видите сложность изменить свойство объекта?


                  1. gen1lee
                    19.04.2024 18:10

                    передавать объект это типичный говнокод, с которым отвалится deep linking например


                    1. mayorovp
                      19.04.2024 18:10

                      С фига ли говнокод и при чём тут вообще deep linking?


                    1. Alexandroppolus
                      19.04.2024 18:10
                      +1

                      В тех случаях, где с передачей объектов возникнут проблемы, можно передавать id. Но вовсе не потому, что инструмент, видите ли, без этого не может нормально работать.


                      1. gen1lee
                        19.04.2024 18:10

                        Передавать в экран объект вместо id это плохо всегда. Между вложенными компонентами допустимо. И да, инструменты тут ни при чем.


                      1. Alexandroppolus
                        19.04.2024 18:10
                        +1

                        Я допускаю, что это плохо в некоторых отдельных случаях. Но по поводу "всегда" интересно увидеть обоснование.


                      1. gen1lee
                        19.04.2024 18:10

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


                      1. mayorovp
                        19.04.2024 18:10

                        В какой момент вы перешли от компонента к экрану?


                  1. Alexandroppolus
                    19.04.2024 18:10

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


              1. gen1lee
                19.04.2024 18:10

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


      1. gen1lee
        19.04.2024 18:10

        У вас слабое знание предмета - библиотеки redux. Там можно делать несколько сторов. И да, у 99.9% приложений никаких проблем с оптимизацией не возникнет даже с одним стором.


      1. gen1lee
        19.04.2024 18:10

        В redux для асинхронищины вообще не обязательно что либо использовать. Просто мало кто об этом знает.


        1. healthxp1
          19.04.2024 18:10

          А кто говорит, что обязательно? Также как и то, что технически невозможно создать несколько сторов?

          Скорее здесь был описан наиболее стандартный пример: стор один и он глобален, для взаимодействия с API / других асинхронных операций используется какая-либо библиотека из указанных выше (что как раз таки во многом позволяет сокращать кол-во бойлерплейта, который и оценивается). А насчет перформанса Redux даже его дока не дает однозначного ответа (с одной стороны да, многие приложения не будут сталкиваться с проблемами оптимизации, но в то де время другие библиотеки могут работать эффективнее, без траты времени на кастомизацию и профилирование): https://redux.js.org/faq/performance#how-well-does-redux-scale-in-terms-of-performance-and-architecture

          P.S. И да, как грамотно ответил кто-то в комментах, говоря про бойлерплейт, для указанного примера самым лаконичным был бы React Query / SWR)


          1. gen1lee
            19.04.2024 18:10

            Критика боилерплейта redux это почти всегда рассуждения на примерах типа hello world. Вот только во-первых его можно очень неплохо "причесать", а во-вторых - с ростом проекта он не увеличивается, в отличие от MobX, где растет количество связей, появляются нетипичные проблемы, решать которые просто не получится, такие как сериализация, или инициализация большого куска данных из сохраненного состояния. В redux состояние это всегда максимально тупая структура, которую можно целиком стерилизовать и десериализовать при желании, а нетипичные ситуации практически отсутствуют - на все есть продуманное, популярное решение. А для mobx даже появился mobx-state-tree, чтобы решить эту архитектурную проблему.

            Наиболее стандартный пример с одним стором потому и стандартный, потому что на практике это отлично работает, и даже не требуется разбивать на сторы, а для оптимизации достаточно грамотное использование и какой нибудь reselect, и только если нужно совсем хорошо, можно обратиться к нормализации (об этом и написано в доке), и созданию дополнительных сторов, и даже использование React.Context для какой нибудь темы. А практика вообще показывает, что в 95% случаев узкое горлышко это как раз не redux, а неоптимальный код на react, и соответственно выбирать технологию нужно изходя из того, какая проще. Redux намного проще, а значит лучше.


  1. DarthVictor
    19.04.2024 18:10
    +1

    На прошлой неделе впервые поучаствовал в конференции по Frontend, где один из докладчиков, расказывал, как удачно его команда переехала с Redux на Mobx. 

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

    Пример в статье сильно маленький и не даёт особого представления о разработке. На таком примере Zustand будет короче обоих вариантов, а SWR или @tanstack/react-query вообще обойдутся парой строк для описания всего взаимодействия с сервером. Причём у последних реализация ещё и будет корректнее.

    В реальном приложении будут играть роль такие моменты как:

    • Удобство композиции сторов. Использование одного действия для изменения нескольких сторов, например.

    • Удобство взаимодействие c компонентами, насколько библиотека opinionated (в данном контексте насколько она влияет на архитектуру приложения).

    • Оптимизация быстродействия. "Удобные" селекторы в Redux, например.

    • Взаиможействие с локальным стейтом Зачеркнул, потому что в React он сделан так, что в любой библиотеке это будет примерно одинаково ху..во.

    • Взаимодействие с библиотеками для работы с сервером вроде SWR и @tanstack/react-query, либо наличие чего-то аналогичного в экосистеме.

    • Тестируемость.

    • Простота отладки и логгирования, в том числе в проде.

    • Взаимодействие с серверным рендерингом.

    • Удобство работы со сложным асинхронным кодом (всякие саги у Redux и экшены на генераторах у MobX).

    • Реализация шаблона внедрения зависимости, либо взаимодействие со встроенным в React внедрением зависимости через Context.


    1. Modin
      19.04.2024 18:10
      +1

      Откуда? Какие такие экшены на генераторах в Mobx? Mobx 5 это ванильный js/ts код за исключением, может быть makeAutoObservable в конструкторе класса. Все. После этого делайте хоть на генераторах, хоть на промисах, хоть на async/await, никто не диктует условий


      1. Modin
        19.04.2024 18:10
        +1

        Конечно, внутри, все устроено сложнее, но это скрыто от пользователя, по факту взаимодействовать придется с почти ванильным js


      1. mayorovp
        19.04.2024 18:10

        Никакое чудо не даст какой-то там библиотеке внедряться в механизм async/await, хоть десять версий выпусти. А внедряться надо, чтобы избегать лишних рендеров.

        Так что либо генераторы, либо особый плагин к транспилятору.


      1. DarthVictor
        19.04.2024 18:10

        Которые flow

        И генераторы это ванильный код со времен ES2015


  1. markelov69
    19.04.2024 18:10
    +1

    Вместо тысячи слов и споров, всё максимально наглядно и очевидно для всех.
    Вот прям пример с MobX (меньше 100 строк кода), тут есть и глобальное состояние и локальное состояние компонента:
    https://codesandbox.io/p/sandbox/adoring-banach-k8m9ss?file=%2Fsrc%2FApp.tsx

    Реализуйте тоже самое, только используя Redux/RTK и приложите ссылку на codesandbox и сравним все вместе какой код приятнее, с RTK или с MobX. Критерий оценки не кол-во строк кода, а то, насколько код читаем, легко понятен и очевиден.


    1. markelov69
      19.04.2024 18:10

      @artemmorozov13 ну так что, код будет?


  1. artalar
    19.04.2024 18:10
    +1

    А если еще чуть в сторонку посмотреть...
    https://github.com/artalar/RTK-entities-basic-example/pull/1/files


    1. nin-jin
      19.04.2024 18:10

      Так всё же более понятно:

      import { $mol_wire_solo as mem, $mol_wire_method as act, $mol_wire_sync as sync } from 'mol_wire_lib'
      import userAPI from './userAPI'
      
      export class User extends Object {
        
          @mem static all( next?: User[] ) {
              return next ?? sync( userAPI ).fetchAll().data
          }
        
          @mem static one( id: string ) {
              return this.all().find( user => user.id === id )
          }
        
          @act static del( id: string ) {
              this.all( this.all().filter( user => user.id !== id ) )
          }
        
      }

      А если говорить про редакс, то разница вообще на порядок.


  1. hash_buy_yourself
    19.04.2024 18:10

    Крайне неграмотный подход, в примерах используются разные подходы, например типы в редакс другие, а правильнее использовать такие же если мы хотим сравнить количество кода, в примере с компонентом тоже самое при использовании мобыкс почему то был использован switch case, можно было написать так же как и в примере с редаксом