Redux это бойлерплейт, а Mobx нет! Но есть нюанс

—

от автора

На прошлой неделе впервые поучаствовал в конференции по 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 точно нет. Для себя я все таки сделаю вывод, что оба инструмента не заставляют разработчика писать «тону» шаблонного кода.


ссылка на оригинал статьи https://habr.com/ru/articles/809075/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *