UNPKG

@silicon.js/app-state

Version:

A lightweight React Native package for tracking and managing application state changes with a simple provider-based architecture.

333 lines (243 loc) 7.97 kB
# App State Package A lightweight React Native package for tracking and managing application state changes with a simple provider-based architecture. ## Features - 📱 Track app state transitions (active, background, inactive) - 🔢 Count state changes automatically - 🎯 Simple React Context API - ⚡ Minimal performance overhead - 🔄 Automatic cleanup and memory management - 🎨 TypeScript support ## Installation This package uses React Native's built-in `AppState` API, so no additional dependencies are required beyond React Native itself. ## Setup ### 1. Wrap your app with the provider ```tsx import { AppStateProvider } from './app-state'; function App() { return <AppStateProvider>{/* Your app components */}</AppStateProvider>; } ``` ### 2. Use the hook in your components ```tsx import { useAppStateContext } from './app-state'; function MyComponent() { const { state, count } = useAppStateContext(); useEffect(() => { if (state === 'active') { console.log('App came to foreground'); // Refresh data, restart timers, etc. } else if (state === 'background') { console.log('App went to background'); // Pause timers, save state, etc. } }, [state]); return ( <View> <Text>Current State: {state}</Text> <Text>State Changes: {count}</Text> </View> ); } ``` ## API Reference ### AppStateProvider Props | Prop | Type | Required | Description | | -------------- | -------------------------------- | -------- | ---------------------------------------------------- | | `initialCount` | `number` | No | Initial count value (default: `0`) | | `initialState` | `AppStateStatus` | No | Initial app state (default: `AppState.currentState`) | | `methods` | `ReturnType<typeof useAppState>` | No | Override default methods for testing | | `children` | `React.ReactNode` | Yes | Child components | ### Context Values #### State - `state: AppStateStatus` - Current app state (`'active' | 'background' | 'inactive'`) - `count: number` - Number of state changes since initialization (starts at 1) ### App State Values The `state` value can be one of: - `'active'` - App is in the foreground and running - `'background'` - App is in the background (iOS only, Android uses inactive) - `'inactive'` - App is transitioning between foreground and background states ## Usage Examples ### Basic State Tracking ```tsx import { useAppStateContext } from './app-state'; function StatusIndicator() { const { state } = useAppStateContext(); return ( <View> <Text>App is: {state}</Text> {state === 'active' && <Badge color="green">Active</Badge>} {state === 'background' && <Badge color="gray">Background</Badge>} </View> ); } ``` ### Refresh Data on Foreground ```tsx import { useAppStateContext } from './app-state'; function DataList() { const { state } = useAppStateContext(); const [data, setData] = useState([]); useEffect(() => { if (state === 'active') { fetchData().then(setData); } }, [state]); return <FlatList data={data} />; } ``` ### Track State Change Frequency ```tsx import { useAppStateContext } from './app-state'; function StateChangeCounter() { const { count, state } = useAppStateContext(); return ( <View> <Text>State changed {count} times</Text> <Text>Current: {state}</Text> </View> ); } ``` ### Pause/Resume Operations ```tsx import { useAppStateContext } from './app-state'; function Timer() { const { state } = useAppStateContext(); const [isPaused, setIsPaused] = useState(false); useEffect(() => { setIsPaused(state !== 'active'); }, [state]); return ( <View> <Text>{isPaused ? 'Paused' : 'Running'}</Text> </View> ); } ``` ### Auto-save on Background ```tsx import { useAppStateContext } from './app-state'; function Editor() { const { state } = useAppStateContext(); const [content, setContent] = useState(''); useEffect(() => { if (state === 'background' || state === 'inactive') { saveContentToStorage(content); } }, [state, content]); return <TextInput value={content} onChangeText={setContent} placeholder="Type something..." />; } ``` ## Advanced Usage ### Custom Initial Values ```tsx <AppStateProvider initialCount={5} initialState="background"> <App /> </AppStateProvider> ``` ### Testing with Custom Methods ```tsx const mockMethods = { count: 10, state: 'background' as AppStateStatus, }; <AppStateProvider methods={mockMethods}> <ComponentUnderTest /> </AppStateProvider>; ``` ### Multiple Consumers ```tsx function AppContainer() { return ( <AppStateProvider> <Header /> {/* Uses state for status indicator */} <MainContent /> {/* Uses state to refresh data */} <Footer /> {/* Uses count for analytics */} </AppStateProvider> ); } ``` ## Common Use Cases ### 1. **Data Synchronization** Refresh data from API when app returns to foreground: ```tsx useEffect(() => { if (state === 'active') { syncData(); } }, [state]); ``` ### 2. **Analytics Tracking** Track app engagement and session duration: ```tsx useEffect(() => { if (state === 'active') { analytics.trackSessionStart(); } else { analytics.trackSessionEnd(); } }, [state]); ``` ### 3. **Resource Management** Pause heavy operations when app is backgrounded: ```tsx useEffect(() => { if (state === 'background') { pauseVideoPlayback(); stopLocationTracking(); } else if (state === 'active') { resumeVideoPlayback(); startLocationTracking(); } }, [state]); ``` ### 4. **State Persistence** Auto-save user work when app goes to background: ```tsx useEffect(() => { if (state !== 'active') { saveUserState(); } }, [state]); ``` ## Platform Differences ### iOS - Supports all three states: `active`, `inactive`, and `background` - `inactive` occurs during phone calls, lock screen, or app switching - `background` means app is fully backgrounded ### Android - Primarily uses `active` and `inactive` - May not reliably trigger `background` state **Recommendation:** Handle both `background` and `inactive` for cross-platform compatibility. ## Performance Considerations - The package uses a single `AppState` listener shared across all consumers - State changes are debounced to prevent duplicate updates - Listener is automatically cleaned up when provider unmounts - Minimal re-renders - only updates when state actually changes ## Best Practices 1. **Handle all state values** - Don't assume only `active` and `background` 2. **Cleanup side effects** - Stop timers, cancel requests when app backgrounds 3. **Save critical data** - Persist important state before app is suspended 4. **Optimize background tasks** - Minimize processing when app isn't visible 5. **Test on real devices** - App state behavior varies between simulators and devices ## TypeScript Support The package is fully typed with TypeScript: ```typescript interface UseAppStateReturn { count: number; state: AppStateStatus; // 'active' | 'background' | 'inactive' } interface UseAppStateProps { initialCount?: number; initialState?: AppStateStatus; } ``` ## Troubleshooting ### Count starts at 1, not 0 This is intentional - the initial state counts as the first state, so `count` starts at 1. ### State not updating - Ensure component is wrapped in `AppStateProvider` - Test on a real device (simulators may behave differently) - Check that you're using `useAppStateContext()` not `useAppState()` directly ### Multiple state changes Some platforms may trigger rapid state changes during transitions. This is normal behavior and the package handles it correctly by tracking the previous state.