@silicon.js/app-state
v1.0.8
Published
A lightweight React Native package for tracking and managing application state changes with a simple provider-based architecture.
Maintainers
Readme
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
import { AppStateProvider } from './app-state';
function App() {
return <AppStateProvider>{/* Your app components */}</AppStateProvider>;
}2. Use the hook in your components
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
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
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
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
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
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
<AppStateProvider initialCount={5} initialState="background">
<App />
</AppStateProvider>Testing with Custom Methods
const mockMethods = {
count: 10,
state: 'background' as AppStateStatus,
};
<AppStateProvider methods={mockMethods}>
<ComponentUnderTest />
</AppStateProvider>;Multiple Consumers
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:
useEffect(() => {
if (state === 'active') {
syncData();
}
}, [state]);2. Analytics Tracking
Track app engagement and session duration:
useEffect(() => {
if (state === 'active') {
analytics.trackSessionStart();
} else {
analytics.trackSessionEnd();
}
}, [state]);3. Resource Management
Pause heavy operations when app is backgrounded:
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:
useEffect(() => {
if (state !== 'active') {
saveUserState();
}
}, [state]);Platform Differences
iOS
- Supports all three states:
active,inactive, andbackground inactiveoccurs during phone calls, lock screen, or app switchingbackgroundmeans app is fully backgrounded
Android
- Primarily uses
activeandinactive - May not reliably trigger
backgroundstate
Recommendation: Handle both background and inactive for cross-platform compatibility.
Performance Considerations
- The package uses a single
AppStatelistener 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
- Handle all state values - Don't assume only
activeandbackground - Cleanup side effects - Stop timers, cancel requests when app backgrounds
- Save critical data - Persist important state before app is suspended
- Optimize background tasks - Minimize processing when app isn't visible
- Test on real devices - App state behavior varies between simulators and devices
TypeScript Support
The package is fully typed with 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()notuseAppState()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.
