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

@longsien/react-store

v1.2.2

Published

A lightweight, proxy-based state management library for React.

Readme

React Store

A lightweight, proxy-based global state management library for React.

Features

  • Lightweight: Minimal footprint with zero dependencies beyond React
  • Proxy-based: JavaScript Proxy enables nested property access with path tracking
  • Dynamic Scoping: Components automatically subscribe only to specific array indices or object properties they access

Installation

npm install @longsien/react-store

Quick Start

import { store, useStore } from '@longsien/react-store'

// Create a store with initial value
const counterStore = store(0)

function Counter() {
  // Get current value and setter function
  const [count, setCount] = useStore(counterStore)

  return (
    <div>
      <p>Count: {count}</p>
      {/* Direct value update */}
      <button onClick={() => setCount(count + 1)}>+</button>
      {/* Function-based update */}
      <button onClick={() => setCount(prev => prev - 1)}>-</button>
    </div>
  )
}

As Global Store

You can treat a store as a global store and use it in any component without prop passing and unnecessary re-renders.

import { store, useStore, useStoreValue } from '@longsien/react-store'

// Global store accessible from any component
const personStore = store({ name: 'Hanni', origin: 'Australia' })

// Component that updates store values
function Updater() {
  // Subscribe to specific nested properties
  const [name, setName] = useStore(personStore.name)
  const [origin, setOrigin] = useStore(personStore.origin)

  return (
    <div>
      <input type='text' value={name} onChange={e => setName(e.target.value)} />
      <input
        type='text'
        value={origin}
        onChange={e => setOrigin(e.target.value)}
      />
    </div>
  )
}

// Read-only component for name
function DisplayName() {
  // Only re-renders when name changes
  const name = useStoreValue(personStore.name)

  return <div>Name: {name}</div>
}

// Read-only component for origin
function DisplayOrigin() {
  // Only re-renders when origin changes
  const origin = useStoreValue(personStore.origin)

  return <div>Origin: {origin}</div>
}

API Reference

Store Creation

store(initialValue)

Creates a basic in-memory store that persists for the lifetime of the application session.

// Basic in-memory store
const userStore = store({ name: 'Winter', origin: 'South Korea' })

store(initialValue).local(key)

Creates a store backed by localStorage with automatic persistence. Data is automatically serialized to JSON when saving and deserialized when loading.

// Store with localStorage persistence
const settingsStore = store({ theme: 'dark' }).local('settings')

store(initialValue).session(key)

Creates a store backed by sessionStorage with automatic persistence. Data is automatically serialized to JSON when saving and deserialized when loading.

// Store with sessionStorage persistence
const tempStore = store({ items: [] }).session('temp-data')

Hooks

useStore(store)

Returns [value, setState] tuple for reading and updating state. Use exactly the same as React's built-in useState hook.

// Returns [value, setter] tuple like useState
const [user, setUser] = useStore(userStore)
const [userName, setUserName] = useStore(userStore.name)
const [userOrigin, setUserOrigin] = useStore(userStore.origin)

useStoreValue(store)

Returns only the current value (read-only).

// Read-only access, no setter returned
const user = useStoreValue(userStore)
const userName = useStoreValue(userStore.name)
const userOrigin = useStoreValue(userStore.origin)

useStoreSetter(store)

Returns only the setter function, avoiding unnecessary re-renders when the value changes.

// Only get setter function, avoids re-renders
const setUser = useStoreSetter(userStore)
const setUserName = useStoreSetter(userStore.name)
const setUserOrigin = useStoreSetter(userStore.origin)

Non-Hook Functions

store.get()

Get current value outside React components. Useful for utility functions, event handlers, or any code that runs outside the React render cycle.

// Get values outside React components
const currentUser = userStore.get()
const userName = userStore.name.get()
const userOrigin = userStore.origin.get()

store.set(value)

Update value outside React components. Triggers all subscribed components to re-render if their specific data has changed. Accepts the same value types as the hook-based setters.

// Update values outside React components
useStore.set({ name: 'Karina', origin: 'South Korea' })
useStore.name.set('Ningning')
useStore.origin.set('China')

Derived Stores

Derived stores automatically compute values based on other stores and update when their dependencies change.

Basic Derived Stores

import { store, useStore } from '@longsien/react-store'

// Base store
const counterStore = store(0)
// Derived from counterStore
const doubledStore = counterStore.derive(count => count * 2)
// Derived from doubledStore (chained derivation)
const doubledAgainStore = doubledStore.derive(count => count * 2)

function Counter() {
  const [count, setCount] = useStore(counterStore)
  const [doubled] = useStore(doubledStore)
  const [doubledAgain] = useStore(doubledAgainStore)

  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled: {doubled}</p>
      <p>Doubled Again: {doubledAgain}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  )
}

Multi-Dependency Derived Stores

import { store, useStore } from '@longsien/react-store'

// Multiple independent stores
const nameStore = store('Winter')
const originStore = store('South Korea')
const isActiveStore = store(true)

// Derived store combining multiple dependencies
const userProfileStore = store(get => ({
  name: get(nameStore),
  origin: get(originStore),
  isActive: get(isActiveStore),
  // Computed values based on dependencies
  displayName: `${get(nameStore)} (${get(originStore)})`,
  status: get(isActiveStore) ? 'Online' : 'Offline',
  canPerformActions: get(isActiveStore) && get(originStore) !== 'Unknown',
}))

function UserProfile() {
  // Automatically updates when any dependency changes
  const [userProfile] = useStore(userProfileStore)

  return (
    <div>
      <h3>{userProfile.displayName}</h3>
      <p>Status: {userProfile.status}</p>
      <p>Can perform actions: {userProfile.canPerformActions ? 'Yes' : 'No'}</p>
    </div>
  )
}

Async Stores

Async stores handle asynchronous operations with built-in loading, error, and success states.

Basic Async Store

import { store, useStoreValue, isSuccess } from '@longsien/react-store'

// Async store that fetches data on creation
const pokemonStore = store().async(() =>
  fetch(`https://pokeapi.co/api/v2/pokemon/pikachu`).then(res => res.json())
)

function Pokemon() {
  const pokemon = useStoreValue(pokemonStore)

  // Check if data is successfully loaded
  return <div>Pokemon: {isSuccess(pokemon) && pokemon.name}</div>
}

Async Derived Store

import {
  store,
  useStore,
  isLoading,
  isError,
  isSuccess,
  getErrorMessage,
} from '@longsien/react-store'

// Store for Pokemon ID
const pokemonIdStore = store(1)
// Async derived store that fetches when ID changes
const pokemonDetailsStore = pokemonIdStore.derive(async id => {
  const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`)
  return response.json()
})

function PokemonDetails() {
  const [pokemonId, setPokemonId] = useStore(pokemonIdStore)
  const [pokemonDetails] = useStore(pokemonDetailsStore)

  return (
    <div>
      <button onClick={() => setPokemonId(pokemonId + 1)}>Next Pokemon</button>

      {/* Show loading state */}
      {isLoading(pokemonDetails) && <p>Loading Pokemon details...</p>}
      {/* Show error state */}
      {isError(pokemonDetails) && (
        <p>Error: {getErrorMessage(pokemonDetails)}</p>
      )}
      {/* Show success state */}
      {isSuccess(pokemonDetails) && (
        <div>
          <h3>{pokemonDetails.name}</h3>
          <p>
            <img
              src={pokemonDetails.sprites.front_default}
              alt={pokemonDetails.name}
            />
          </p>
          <p>Height: {pokemonDetails.height}</p>
          <p>Weight: {pokemonDetails.weight}</p>
        </div>
      )}
    </div>
  )
}

Async Utility Functions

isLoading(data)

Returns true if the async store is currently loading.

// Check if async operation is in progress
{
  isLoading(pokemonDetails) && <p>Loading Pokemon...</p>
}

isError(data)

Returns true if the async operation failed.

import { isError, getErrorMessage } from '@longsien/react-store'

{
  isError(pokemonDetails) && <p>Error: {getErrorMessage(pokemonDetails)}</p>
}

isSuccess(data)

Returns true if the async operation completed successfully.

// Check if async operation succeeded
{
  isSuccess(pokemonDetails) && <div>{/* Render success content */}</div>
}

getErrorMessage(data)

Returns the error message from a failed async operation.

// Extract error message from failed async operation
const errorMessage = getErrorMessage(pokemonDetails)

getErrorStatus(data)

Returns the HTTP status code from a failed async operation.

// Extract HTTP status code from failed async operation
const statusCode = getErrorStatus(pokemonDetails)

Nested Property Access

The library uses JavaScript Proxies to enable nested property access. This allows components to subscribe to deeply nested values without re-rendering when unrelated parts of the state change.

import { store, useStore } from '@longsien/react-store'

// Nested object structure
const userStore = store({
  profile: {
    name: 'Winter',
    origin: 'South Korea',
    settings: { theme: 'dark' },
  },
  posts: [],
})

// Subscribe to specific nested properties
const [theme, setTheme] = useStore(userStore.profile.settings.theme)
const [origin, setOrigin] = useStore(userStore.profile.origin)
const [posts, setPosts] = useStore(userStore.posts)

// Updates only affect components using those specific paths
setTheme('light') // Only theme subscribers re-render
setOrigin('Australia') // Only origin subscribers re-render
setPosts(prev => [...prev, newPost]) // Only posts subscribers re-render

Dynamic Scoping

Nested property access works with dynamic scoping, allowing dynamic path path subscription based on component props.

Array Index Subscriptions

import { useStore, useStoreValue } from '@longsien/react-store'

// Dynamic array index subscription
const [comment, setComment] = useStore(commentsStore[index])
const author = useStoreValue(commentsStore[index].author)

Dynamic Object Property Subscriptions

import { useStore, useStoreSetter } from '@longsien/react-store'

// Dynamic object property subscription
const [user, setUser] = useStore(usersStore[userId])
const setStatus = useStoreSetter(usersStore[userId].status)

Requirements

  • React 18.0.0 or higher

License

MIT

Contributing

Issues and pull requests are welcome on GitHub.

Author

Long Sien