Skip to main content

react-native-state-management

Manages application state in React Native using Context API, Zustand, Redux Toolkit, and Jotai with proper persistence, middleware, and re-render optimization for mobile.

الانتقال إلى التثبيت

معلومات المصدر

المستودع
paulpas/agent-skill-router
آخر نشاط في المصدر
٩ يونيو ٢٠٢٦ في ١٦:٢٨
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٤
التفرعات
١

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
react-native-state-management
description
Manages application state in React Native using Context API, Zustand, Redux Toolkit, and Jotai with proper persistence, middleware, and re-render optimization for mobile.
license
MIT
compatibility
opencode
archetypes
["tactical","generation"]
anti_triggers
["brainstorming","vague ideation","long-form architecture"]
response_profile
{"verbosity":"low","directive_strength":"high","abstraction_level":"operational"}
metadata
{"version":"1.0.0","domain":"coding","triggers":"react native state, state management, zustand, redux toolkit, jotai, asyncstorage, react native data","role":"implementation","scope":"implementation","output-format":"code","content-types":["code","guidance","do-dont","examples"],"related-skills":"react-native-rendering, react-native-monorepo-config","author":"https://github.com/vercel-labs","source":"https://github.com/vercel-labs/agent-skills"}
# React Native State Management A senior React Native engineer selecting and implementing state management solutions (Context API, Zustand, Redux Toolkit, Jotai) with persistence, middleware, and performance optimization for mobile applications. ## TL;DR Checklist - [ ] Evaluate app complexity before choosing a state management library - [ ] Use Zustand or Jotai for medium-complexity apps (minimal boilerplate, great DX) - [ ] Use Redux Toolkit with RTK Query for complex apps needing API caching and devtools - [ ] Implement state persistence with AsyncStorage + middleware layer - [ ] Use selectors with equality checks (useShallow, shallow) to prevent re-renders - [ ] Code-split stores by feature domain for large applications - [ ] Reset all state on auth logout via a single action - [ ] Keep state flat — never store derived/computed data --- ## When to Use Use this skill when: - Building a new React Native app and deciding on a state management strategy - Refactoring a component that re-renders too often due to global state subscriptions - Implementing offline-first behavior with persisted state - Adding auth flows that require state reset on logout - Setting up Redux Toolkit with RTK Query for API caching - Introducing Zustand as a lightweight alternative to Redux --- ## When NOT to Use Avoid this skill for: - Simple apps with minimal shared state — React's built-in `useState` + prop drilling is sufficient - Web-only React projects (no AsyncStorage dependency) — use localStorage or similar instead - Server-state dominated apps where React Query or SWR alone suffices (no client state needed) - Performance-critical animations driven by state — use Reanimated shared values instead --- ## Core Workflow 1. **Assess App Complexity** — Determine the right approach: - **Simple** (1-2 shared values) → Context API + `useReducer` - **Medium** (multi-feature, moderate state) → Zustand or Jotai - **Complex** (many features, API caching, middleware) → Redux Toolkit + RTK Query **Checkpoint:** If you need API caching, choose RTK Query or React Query before adding a state library. 2. **Define State Shape** — Design flat, normalized state slices: - Keep state as flat as possible — avoid deeply nested objects - Never store derived data (filtered lists, computed totals) — compute with selectors - Separate server state (API data) from client state (UI toggles, form inputs) **Checkpoint:** Can every piece of state be serialized to JSON? If not, move non-serializable data to a ref or middleware. 3. **Implement Store with Selectors** — Create the store/slice with memoized selectors: - Use `createSelector` (Redux) or `useShallow` (Zustand) for equality checks - Code-split by feature — one store/slice per app domain - Keep actions and reducers colocated with their feature **Checkpoint:** Verify that components only re-render when their specific slice of state changes. 4. **Add Persistence** — Wire AsyncStorage with the appropriate middleware: - Redux: `redux-persist` with `AsyncStorage` engine - Zustand: `persist` middleware - Jotai: `atomWithStorage` from `jotai/utils` - Only persist essential user data (auth tokens, preferences, drafts) — never cache full API responses **Checkpoint:** Test that state survives app kill/restart on both iOS and Android. 5. **Handle Auth Flow** — Implement login/logout with proper state lifecycle: - On login: restore persisted state or initialize fresh - On logout: dispatch a single `RESET_STATE` action or call `store.reset()` - Never manually clear individual slices — one atomic reset prevents stale data bugs **Checkpoint:** Verify that after logout, no previous user's data is accessible. --- ## Implementation Patterns ### Pattern 1: Zustand Store with Persistence (Medium Complexity) ```tsx import { create } from 'zustand'; import { persist, createJSONStorage } from 'zustand/middleware'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { useShallow } from 'zustand/react/shallow'; // --- State shape --- interface UserPreferences { theme: 'light' | 'dark' | 'system'; fontSize: number; notificationsEnabled: boolean; } interface AppState { // Auth userId: string | null; token: string | null; // Preferences (persisted) preferences: UserPreferences; // Actions login: (userId: string, token: string) => void; logout: () => void; updatePreferences: (prefs: Partial<UserPreferences>) => void; } const initialState = { userId: null, token: null, preferences: { theme: 'system' as const, fontSize: 16, notificationsEnabled: true, }, }; // --- Zustand store with persistence --- export const useAppStore = create<AppState>()( persist( (set) => ({ ...initialState, login: (userId, token) => set({ userId, token }), logout: () => set(initialState), // Single atomic reset updatePreferences: (prefs) => set((state) => ({ preferences: { ...state.preferences, ...prefs }, })), }), { name: 'app-state', storage: createJSONStorage(() => AsyncStorage), // Only persist auth tokens and preferences — skip transient UI state partialize: (state) => ({ userId: state.userId, token: state.token, preferences: state.preferences, }), } ) ); // --- Selector with shallow comparison prevents unnecessary re-renders --- export function useTheme() { return useAppStore(useShallow((state) => ({ theme: state.preferences.theme, fontSize: state.preferences.fontSize, }))); } ``` ### Pattern 2: Redux Toolkit with RTK Query (Complex App) ```tsx import { configureStore, createSlice, createSelector, PayloadAction } from '@reduxjs/toolkit'; import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { persistStore, persistReducer, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER, } from 'redux-persist'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; // --- API slice (server state) --- interface Post { id: number; title: string; body: string; } export const postsApi = createApi({ reducerPath: 'postsApi', baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com' }), endpoints: (builder) => ({ getPosts: builder.query<Post[], void>({ query: () => '/posts', }), getPost: builder.query<Post, number>({ query: (id) => `/posts/${id}`, }), }), }); export const { useGetPostsQuery, useGetPostQuery } = postsApi; // --- Client state slice (UI-only state) --- interface UIState { selectedPostId: number | null; isGridView: boolean; } const uiSlice = createSlice({ name: 'ui', initialState: { selectedPostId: null, isGridView: false, } as UIState, reducers: { selectPost(state, action: PayloadAction<number | null>) { state.selectedPostId = action.payload; }, toggleView(state) { state.isGridView = !state.isGridView; }, }, }); export const { selectPost, toggleView } = uiSlice.actions; // --- Memoized selector --- const selectIsGridView = (state: RootState) => state.ui.isGridView; export const selectLayoutMode = createSelector( [selectIsGridView], (isGridView) => (isGridView ? 'grid' : 'list' as const) ); // --- Persist only UI slice (RTK Query manages its own cache) --- const persistConfig = { key: 'ui', storage: AsyncStorage, whitelist: ['isGridView'], // Don't persist selectedPostId — it's transient }; const persistedUIReducer = persistReducer(persistConfig, uiSlice.reducer); // --- Store --- export const store = configureStore({ reducer: { [postsApi.reducerPath]: postsApi.reducer, ui: persistedUIReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }).concat(postsApi.middleware), }); export const persistor = persistStore(store); export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch; export const useAppDispatch: () => AppDispatch = useDispatch; export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; ``` ### Pattern 3: Jotai for Fine-Grained Atomic State ```tsx import { atom, useAtom, useAtomValue } from 'jotai'; import { atomWithStorage, splitAtom } from 'jotai/utils'; import AsyncStorage from '@react-native-async-storage/async-storage'; // --- Persisted atoms --- export const themeAtom = atomWithStorage<'light' | 'dark'>( 'theme', 'light', { getItem: async (key) => { const value = await AsyncStorage.getItem(key); return value === 'dark' ? 'dark' : 'light'; }, setItem: async (key, value) => { await AsyncStorage.setItem(key, value); }, removeItem: async (key) => { await AsyncStorage.removeItem(key); }, } ); // --- Derived atom (no derived data stored in state) --- export const isDarkAtom = atom((get) => get(themeAtom) === 'dark'); // --- Atom family (splitAtom for dynamic lists) --- interface TodoItem { id: string; text: string; completed: boolean; } export const todosAtom = atom<TodoItem[]>([]); export const todoAtomsAtom = splitAtom(todosAtom); // --- Computed via selectors, not stored --- export const completedTodosAtom = atom((get) => get(todosAtom).filter((todo) => todo.completed) ); export const pendingTodosAtom = atom((get) => get(todosAtom).filter((todo) => !todo.completed) ); // --- Component usage (only re-renders when specific atom changes) --- function ThemeToggle() { const [theme, setTheme] = useAtom(themeAtom); const isDark = useAtomValue(isDarkAtom); return ( <button onPress={() => setTheme(theme === 'light' ? 'dark' : 'light')}> Current: {theme} {isDark ? '🌙' : '☀️'} </button> ); } ``` ### Pattern 4: Context API Pitfall — When Not to Use It ```tsx // ❌ BAD: Context for rapidly-updating global state (causes full-tree re-renders) const PositionContext = createContext({ latitude: 0, longitude: 0, heading: 0, speed: 0, }); function PositionProvider({ children }: { children: React.ReactNode }) { const [position, setPosition] = useState({ latitude: 0, longitude: 0, heading: 0, speed: 0 }); useEffect(() => { const sub = Location.watchPositionAsync( { enableHighAccuracy: true, timeInterval: 100 }, (loc) => setPosition({ latitude: loc.coords.latitude, longitude: loc.coords.longitude, heading: loc.coords.heading ?? 0, speed: loc.coords.speed ?? 0, }) ); return () => sub.then((s) => s.remove()); }, []); return <PositionContext.Provider value={position}>{children}</PositionContext.Provider>; } // Every consumer re-renders 10 times/second — even those that only show heading! // ✅ GOOD: Use Zustand for high-frequency updates (only subscribed components re-render) import { create } from 'zustand'; interface PositionState { latitude: number; longitude: number; heading: number; speed: number; setPosition: (pos: Omit<PositionState, 'setPosition'>) => void; } export const usePositionStore = create<PositionState>((set) => ({ latitude: 0, longitude: 0, heading: 0, speed: 0, setPosition: (pos) => set(pos), })); // Only this component re-renders on each GPS update function HeadingDisplay() { const heading = usePositionStore((s) => s.heading); // Subscribes only to `heading` return <Text>Heading: {heading}°</Text>; } ``` --- ## Constraints ### MUST DO - Use selectors with equality checks (useShallow, shallow, createSelector) to prevent unnecessary re-renders - Implement state persistence via AsyncStorage for user preferences, auth tokens, and draft data - Handle auth flow state reset atomically — one action clears everything - Code-split stores by feature domain (auth/, feed/, settings/) in large apps - Keep state serializable at all times — non-serializable data belongs in refs or middleware - Store only the minimum necessary state — derive everything else via selectors ### MUST NOT DO - Store derived/computed data in state — compute with selectors instead - Use Context API for high-frequency global state updates (GPS, websocket data, animation values) - Store full API responses in persisted state — use RTK Query cache or React Query instead - Mix server state (API data) and client state (UI toggles) in the same slice - Reset state by manually clearing individual keys — always use an atomic reset action - Use mutable patterns in Redux Toolkit (the immer-based reducer handles immutability for you) --- ## Related Skills | Skill | Purpose | |---|---| | `react-native-rendering` | Optimizes component rendering in tandem with state management | | `react-native-monorepo-config` | Configures monorepo workspaces for sharing state libraries across packages | --- ## Live References > Authoritative documentation links for React Native state management. - [React Native State Management Overview](https://reactnative.dev/docs/state) — Official React Native guide - [Zustand Documentation](https://github.com/pmndrs/zustand) — Lightweight state management for React - [Redux Toolkit Docs](https://redux-toolkit.js.org/) — Official Redux Toolkit with RTK Query - [Jotai Documentation](https://jotai.org/docs) — Atomic state management for React - [Redux Persist](https://github.com/rt2zz/redux-persist) — Persist and rehydrate Redux state
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub