npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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.

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, 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:

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.