Kodax

إدارة الحالة المتقدمة — React Native المتقدم

إدارة الحالة المتقدمة

1. Zustand (الأسهل والأفضل)

npm install zustand
// stores/useAuthStore.js
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';

const useAuthStore = create(
  persist(
    (set, get) => ({
      user: null,
      token: null,
      isLoading: false,

      login: async (email, password) => {
        set({ isLoading: true });
        try {
          const response = await api.post('/login', { email, password });
          set({
            user: response.user,
            token: response.token,
            isLoading: false,
          });
          return { success: true };
        } catch (error) {
          set({ isLoading: false });
          return { success: false, error: error.message };
        }
      },

      logout: () => {
        set({ user: null, token: null });
      },

      updateUser: (updates) => {
        set((state) => ({
          user: { ...state.user, ...updates },
        }));
      },

      // Selector
      isLoggedIn: () => get().token !== null,
    }),
    {
      name: 'auth-storage',
      storage: createJSONStorage(() => AsyncStorage),
    }
  )
);

// الاستخدام
function ProfileScreen() {
  const { user, logout, updateUser } = useAuthStore();
  const isLoggedIn = useAuthStore((state) => state.isLoggedIn());

  return (
    <View>
      <Text>مرحباً {user?.name}</Text>
      <TouchableOpacity onPress={logout}>
        <Text>تسجيل خروج</Text>
      </TouchableOpacity>
    </View>
  );
}

2. متجر للعربة (Cart Store)

// stores/useCartStore.js
import { create } from 'zustand';

const useCartStore = create((set, get) => ({
  items: [],

  addItem: (product) => {
    const items = get().items;
    const existingItem = items.find((i) => i.id === product.id);

    if (existingItem) {
      set({
        items: items.map((i) =>
          i.id === product.id ? { ...i, quantity: i.quantity + 1 } : i
        ),
      });
    } else {
      set({ items: [...items, { ...product, quantity: 1 }] });
    }
  },

  removeItem: (productId) => {
    set({
      items: get().items.filter((i) => i.id !== productId),
    });
  },

  updateQuantity: (productId, quantity) => {
    if (quantity <= 0) {
      get().removeItem(productId);
      return;
    }
    set({
      items: get().items.map((i) =>
        i.id === productId ? { ...i, quantity } : i
      ),
    });
  },

  clearCart: () => set({ items: [] }),

  // Computed values
  get totalItems() {
    return get().items.reduce((sum, item) => sum + item.quantity, 0);
  },

  get totalPrice() {
    return get().items.reduce(
      (sum, item) => sum + item.price * item.quantity,
      0
    );
  },
}));

// الاستخدام
function CartScreen() {
  const { items, removeItem, updateQuantity, totalPrice } = useCartStore();

  return (
    <FlatList
      data={items}
      renderItem={({ item }) => (
        <View style={styles.cartItem}>
          <Text>{item.name}</Text>
          <Text>{item.price} د.ع</Text>
          <View style={styles.quantityControls}>
            <TouchableOpacity
              onPress={() => updateQuantity(item.id, item.quantity - 1)}
            >
              <Text>-</Text>
            </TouchableOpacity>
            <Text>{item.quantity}</Text>
            <TouchableOpacity
              onPress={() => updateQuantity(item.id, item.quantity + 1)}
            >
              <Text>+</Text>
            </TouchableOpacity>
          </View>
        </View>
      )}
      ListFooterComponent={
        <Text style={styles.total}>المجموع: {totalPrice} د.ع</Text>
      }
    />
  );
}

// في Header
function CartIcon() {
  const totalItems = useCartStore((state) => state.totalItems);

  return (
    <View>
      <Ionicons name="cart" size={24} />
      {totalItems > 0 && (
        <View style={styles.badge}>
          <Text style={styles.badgeText}>{totalItems}</Text>
        </View>
      )}
    </View>
  );
}

3. Redux Toolkit (للمشاريع الكبيرة جداً)

npm install @reduxjs/toolkit react-redux
// store/slices/userSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

export const fetchUser = createAsyncThunk(
  'user/fetchUser',
  async (userId) => {
    const response = await api.get(`/users/${userId}`);
    return response.data;
  }
);

const userSlice = createSlice({
  name: 'user',
  initialState: {
    data: null,
    status: 'idle',
    error: null,
  },
  reducers: {
    setUser: (state, action) => {
      state.data = action.payload;
    },
    clearUser: (state) => {
      state.data = null;
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.status = 'succeeded';
        state.data = action.payload;
      })
      .addCase(fetchUser.rejected, (state, action) => {
        state.status = 'failed';
        state.error = action.error.message;
      });
  },
});

export const { setUser, clearUser } = userSlice.actions;
export default userSlice.reducer;
// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './slices/userSlice';

export const store = configureStore({
  reducer: {
    user: userReducer,
  },
});
// App.js
import { Provider } from 'react-redux';
import { store } from './store';

function App() {
  return (
    <Provider store={store}>
      <Navigation />
    </Provider>
  );
}
// الاستخدام في شاشة
import { useSelector, useDispatch } from 'react-redux';
import { fetchUser } from './store/slices/userSlice';

function UserProfile() {
  const dispatch = useDispatch();
  const { data: user, status, error } = useSelector((state) => state.user);

  useEffect(() => {
    dispatch(fetchUser(1));
  }, [dispatch]);

  if (status === 'loading') return <ActivityIndicator />;
  if (error) return <Text>Error: {error}</Text>;

  return <Text>{user?.name}</Text>;
}
اختبر فهمك