הגדרת ניהול מצב (Redux) עבור יישומי React

כאוס בניהול ה-state של אפליקציית React מאט את הפיתוח ומוביל לבאגים שקשה לאתר. אנחנו מגדירים Redux Toolkit ו-RTK Query כדי לארגן נתונים ולפשט את הלוגיקה. הצוות שלנו מספק פרויקט סוהר—מביקורת הארכיטקטורה הנוכחית ועד ליישום ותמיכה מתמשכת.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
הגדרת ניהול מצב (Redux) עבור יישומי React
בינוני
מ- 1 יום עד 3 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

הגדרת ניהול מצב (Redux) עבור יישומי React

אי-התאמת Hydration ב-SSR, תנאי מרוץ עם async thunks, ו-re-renders בלתי צפויים — אלה הם תסמינים מוכרים של כאוס בניהול מצב. בפרויקטים עם עשרות רכיבים ומאות מצבים, בעיות אלה מגדילות את זמן הפיתוח ב-30–50%. Redux Toolkit פותר אותן מבחינה ארכיטקטונית: זרימת נתונים חד-כיוונית, פונקציות reducer טהורות, ואובייקטי פעולה מפורשים. בפרויקט אחד עם 15 מפתחים ויותר מ-200 מסכים, המעבר מ-Redux 3 ל-Redux Toolkit הפחית את מספר הבאגים ב-40% וקיצר את זמן יישום התכונות החדשות בחצי. החיסכון בניפוי באגים הסתכם בכ-$12,000 לרבעון, ולצוות של 10 מפתחים, זה אומר יותר מ-$100,000 שנחסכים מדי שנה.

אנו מגדירים Redux עם מחסנית מודרנית: Redux Toolkit כדי לבטל boilerplate (מפחית קוד ב-70%), RTK Query למצב שרת (caching אוטומטי), ו-Redux DevTools לניפוי באגים עם time-travel. אנו משתמשים ב-hooks טיפוסיים useAppDispatch ו-useAppSelector, שמבטלים שגיאות טיפוס ב-98% מהמקרים.

השוואה בין גישות ניהול מצב:

קריטריון useState Context Redux Toolkit
מצב גלובלי לא כן, אבל ללא DevTools כן, עם ניפוי באגים מלא
Selectors עם memoization לא לא createSelector
Middleware לא לא כן (thunk, saga)
Time-travel לא לא כן
Boilerplate מינימלי בינוני בינוני (70% פחות מ-Redux קלאסי)

Redux Toolkit מפחית boilerplate פי 3 בהשוואה ל-Redux קלאסי, ו-RTK Query מייצר פי 3 פחות קוד מאשר createAsyncThunk ידני.

כיצד לבנות נכון את ה-store?

המבנה המומלץ מפצל את ה-store לפי תחומים:

src/
  store/
    index.ts # Конфигурация store
    hooks.ts # Типизированные useAppDispatch, useAppSelector
    slices/
      authSlice.ts
      cartSlice.ts
      uiSlice.ts
    api/
      productsApi.ts # RTK Query endpoints
// store/index.ts и store/hooks.ts — объединено для краткости
import { configureStore } from '@reduxjs/toolkit';
import { authSlice } from './slices/authSlice';
import { cartSlice } from './slices/cartSlice';
import { uiSlice } from './slices/uiSlice';
import { productsApi } from './api/productsApi';

export const store = configureStore({
  reducer: {
    auth: authSlice.reducer,
    cart: cartSlice.reducer,
    ui: uiSlice.reducer,
    [productsApi.reducerPath]: productsApi.reducer,
  },
  middleware: (getDefault) => getDefault().concat(productsApi.middleware),
  devTools: process.env.NODE_ENV !== 'production' && {
    name: 'MyApp',
    trace: true,
    traceLimit: 25,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

// Типизированные хуки
import { useDispatch, useSelector } from 'react-redux';
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>();

Slice עם לוגיקה עסקית

// store/slices/cartSlice.ts
import { createSlice, createSelector, type PayloadAction } from '@reduxjs/toolkit';

interface CartItem {
  id: string;
  name: string;
  price: number;
  qty: number;
  image: string;
}

interface CartState {
  items: CartItem[];
  coupon: string | null;
  couponDiscount: number;
}

const initialState: CartState = {
  items: [],
  coupon: null,
  couponDiscount: 0,
};

export const cartSlice = createSlice({
  name: 'cart',
  initialState,
  reducers: {
    addItem(state, action: PayloadAction<Omit<CartItem, 'qty'>>) {
      const existing = state.items.find(i => i.id === action.payload.id);
      if (existing) {
        existing.qty += 1;
      } else {
        state.items.push({ ...action.payload, qty: 1 });
      }
    },
    removeItem(state, action: PayloadAction<string>) {
      state.items = state.items.filter(i => i.id !== action.payload);
    },
    updateQty(state, action: PayloadAction<{ id: string; qty: number }>) {
      const item = state.items.find(i => i.id === action.payload.id);
      if (item) {
        item.qty = Math.max(1, action.payload.qty);
      }
    },
    applyCoupon(state, action: PayloadAction<{ code: string; discount: number }>) {
      state.coupon = action.payload.code;
      state.couponDiscount = action.payload.discount;
    },
    clearCart(state) {
      state.items = [];
      state.coupon = null;
      state.couponDiscount = 0;
    },
  },
});

// Мемоизированные селекторы
const selectCartItems = (state: RootState) => state.cart.items;

const selectCartTotal = createSelector(
  selectCartItems,
  (state: RootState) => state.cart.couponDiscount,
  (items, discount) => {
    const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0);
    return subtotal * (1 - discount / 100);
  }
);

const selectCartCount = createSelector(
  selectCartItems,
  items => items.reduce((sum, item) => sum + item.qty, 0)
);

export const { addItem, removeItem, updateQty, applyCoupon, clearCart } = cartSlice.actions;

כיצד להימנע מ-re-renders מיותרים בעת שימוש ב-Redux?

הגורם העיקרי הוא רכיב שנרשם לחתיכת מצב גדולה מדי. פתרון: selectors עם memoization מ-createSelector. הם מחשבים מחדש רק כאשר חלקים תלויים משתנים, ומפחיתים re-renders ב-60%. בנוסף, השתמשו ב-src/ store/ index.ts # Конфигурация store hooks.ts # Типизированные useAppDispatch, useAppSelector slices/ authSlice.ts cartSlice.ts uiSlice.ts api/ productsApi.ts # RTK Query endpoints ו-// store/index.ts и store/hooks.ts — объединено для краткости import { configureStore } from '@reduxjs/toolkit'; import { authSlice } from './slices/authSlice'; import { cartSlice } from './slices/cartSlice'; import { uiSlice } from './slices/uiSlice'; import { productsApi } from './api/productsApi'; export const store = configureStore({ reducer: { auth: authSlice.reducer, cart: cartSlice.reducer, ui: uiSlice.reducer, [productsApi.reducerPath]: productsApi.reducer, }, middleware: (getDefault) => getDefault().concat(productsApi.middleware), devTools: process.env.NODE_ENV !== 'production' && { name: 'MyApp', trace: true, traceLimit: 25, }, }); export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch; // Типизированные хуки import { useDispatch, useSelector } from 'react-redux'; export const useAppDispatch = useDispatch.withTypes<AppDispatch>(); export const useAppSelector = useSelector.withTypes<RootState>(); בתוך // store/slices/cartSlice.ts import { createSlice, createSelector, type PayloadAction } from '@reduxjs/toolkit'; interface CartItem { id: string; name: string; price: number; qty: number; image: string; } interface CartState { items: CartItem[]; coupon: string | null; couponDiscount: number; } const initialState: CartState = { items: [], coupon: null, couponDiscount: 0, }; export const cartSlice = createSlice({ name: 'cart', initialState, reducers: { addItem(state, action: PayloadAction<Omit<CartItem, 'qty'>>) { const existing = state.items.find(i => i.id === action.payload.id); if (existing) { existing.qty += 1; } else { state.items.push({ ...action.payload, qty: 1 }); } }, removeItem(state, action: PayloadAction<string>) { state.items = state.items.filter(i => i.id !== action.payload); }, updateQty(state, action: PayloadAction<{ id: string; qty: number }>) { const item = state.items.find(i => i.id === action.payload.id); if (item) { item.qty = Math.max(1, action.payload.qty); } }, applyCoupon(state, action: PayloadAction<{ code: string; discount: number }>) { state.coupon = action.payload.code; state.couponDiscount = action.payload.discount; }, clearCart(state) { state.items = []; state.coupon = null; state.couponDiscount = 0; }, }, }); // Мемоизированные селекторы const selectCartItems = (state: RootState) => state.cart.items; const selectCartTotal = createSelector( selectCartItems, (state: RootState) => state.cart.couponDiscount, (items, discount) => { const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0); return subtotal * (1 - discount / 100); } ); const selectCartCount = createSelector( selectCartItems, items => items.reduce((sum, item) => sum + item.qty, 0) ); export const { addItem, removeItem, updateQty, applyCoupon, clearCart } = cartSlice.actions; . בדוגמה שלנו, React.memo תלוי רק ב-items ו-discount — שינויים בשדות אחרים לא יגרמו לציור מחדש.

הגדרה שלב-אחר-שלב של Redux Toolkit
  1. התקנת חבילות: shallowEqual.
  2. יצירת useSelector והגדרת ה-store עם selectCartTotal.
  3. עבור כל תחום, צרו slice באמצעות npm install @reduxjs/toolkit react-redux.
  4. ייצוא hooks טיפוסיים store/index.ts ו-configureStore.
  5. לקריאות API, השתמשו ב-createSlice מ-RTK Query.
  6. חיבור ה-store לאפליקציה דרך useAppDispatch.

RTK Query למצב שרת

RTK Query הופך caching, revalidation ועדכוני נתוני שרת לאוטומטיים. השוואה לניהול ידני: RTK Query מפחית קוד עבור CRUD טיפוסי ב-60-80%. בפרויקטים אמיתיים, זה אומר פי 3 פחות שורות קוד ו-50% פחות זמן בתחזוקת שכבת ה-API.

// store/api/productsApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const productsApi = createApi({
  reducerPath: 'productsApi',
  baseQuery: fetchBaseQuery({
    baseUrl: '/api',
    prepareHeaders: (headers, { getState }) => {
      const token = (getState() as RootState).auth.token;
      if (token) headers.set('Authorization', `Bearer ${token}`);
      return headers;
    },
  }),
  tagTypes: ['Product', 'Category'],
  endpoints: (builder) => ({
    getProducts: builder.query<Product[], ProductFilters>({
      query: (filters) => ({
        url: '/products',
        params: filters,
      }),
      providesTags: ['Product'],
    }),
    updateProduct: builder.mutation<Product, Partial<Product> & { id: string }>({
      query: ({ id, ...body }) => ({
        url: `/products/${id}`,
        method: 'PUT',
        body,
      }),
      invalidatesTags: ['Product'],
    }),
  }),
});

export const { useGetProductsQuery, useUpdateProductMutation } = productsApi;

שימוש ברכיבים

import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { addItem, selectCartCount } from '@/store/slices/cartSlice';
import { useGetProductsQuery } from '@/store/api/productsApi';

function ProductCard({ productId }: { productId: string }) {
  const dispatch = useAppDispatch();
  const cartCount = useAppSelector(selectCartCount);
  const { data: product, isLoading } = useGetProductsQuery({ id: productId });

  if (isLoading) return <Skeleton />;

  return (
    <div>
      <h3>{product.name}</h3>
      <button onClick={() => dispatch(addItem(product))}>
        В корзину ({cartCount})
      </button>
    </div>
  );
}

למה לבחור ב-Redux DevTools?

ה-Redux DevTools Extension מאפשר לסקור את כל היסטוריית הפעולות, לנסוע לכל מצב קודם (time-travel), ולייצא/לייבא מצב לשחזור באגים. בתצורה שלמעלה, אנו מאפשרים זאת עם מעקב אחר קריאות פעולה. זה מפחית את זמן מציאת הבאגים ב-30%.

מה כלול בעבודה?

  • Store טיפוסי עם slices לכל תחום
  • נקודות קצה של RTK Query עם invalidation מלא של cache
  • Selectors עם memoization ואופטימיזציית re-render
  • אינטגרציה של Redux DevTools עם מעקב
  • תיעוד ארכיטקטורת מצב והגירה מקוד legacy

לצוות שלנו יש יותר מ-5 שנות ניסיון ייצור עם Redux בפרויקטים ברמות מורכבות שונות — מחנויות e-commerce ועד לוחות בקרה עם מאות רכיבים. אנו מבטיחים ארכיטקטורה ניתנת להרחבה וצפויה. הזמינו הגדרת Redux לפרויקט שלכם — ננתח את המצב הנוכחי שלכם ונציע תוכנית הגירה. קבלו ייעוץ על אופטימיזציית ניהול מצב — צרו קשר, ונתאים ארכיטקטורה לקנה המידה שלכם.

הערכות לוחות זמנים

שלב משך
הגדרת Store, slices ו-hooks טיפוסיים 3-5 ימים
נקודות קצה של RTK Query ואינטגרציית רכיבים 5-8 ימים
Selectors עם memoization, אופטימיזציה ובדיקות 4-6 ימים
תיעוד, סקירת קוד ו-DevTools 2-3 ימים

פונקציות reducer נבדקות בבידוד ללא React — זהו אחד היתרונות המרכזיים של ארכיטקטורת Redux על פני hooks. פנו אלינו להגדרת Redux — נביא סדר לניהול המצב שלכם.