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 🙏

© 2024 – Pkg Stats / Ryan Hefner

stan-js

v1.0.0

Published

Lightweight and flexible state management library designed for use in React applications and beyond

Downloads

266

Readme

TypeScript React ReactNative MIT NPM Version NPM Downloads

Overview

stan-js is a lightweight and flexible state management library designed for use in React, React Native and even vanilla-js applications. It simplifies the process of managing state in your application by providing a simple createStore function. This package aims to offer a straightforward solution for state management without the need for extensive type definitions or repetitive code.

Features

  • ⚡️ Performance and minimal rerenders
  • ✍️ Simple configuration
  • ⚛️ Out of the box React intergration
  • 🚀 Amazing typescript intellisense
  • 🪝 Easy access to all store values
  • 🪶 Very lightweight

Installation

Install package using preferred package manager:

npm install stan-js
# or
yarn add stan-js
# or
bun add stan-js

Demos

React

Open in repo Open in StackBlitz

Astro + React

Open in repo Open in StackBlitz

Getting Started

Create a store with initial state:

You can think of a store as your app state. You can define multiple keys/values, each key will create separated subscription (more explained here). If you want to persist the value - you can simply wrap it in Synchronizer

import { createStore } from 'stan-js'
import { storage } from 'stan-js/storage'

export const { useStore } = createStore({
    count: 0,
    user: storage(''),
    selectedLanguage: 'en-US',
    unreadNotifications: [] as Array<Notification>
})

Use the returned hook in your React component:

import { useStore } from './store'

const App = () => {
    const { count, user, setCount } = useStore()

    return (
        <div>
            <h1>Hello {user}!</h1>
            <p>Count: {count}</p>
            <button onClick={() => setCount(prev => prev + 1)}>Increment</button>
            <button onClick={() => setCount(prev => prev - 1)}>Decrement</button>
        </div>
    )
}

Check demos to play more with stan-js

Features

import { createStore } from 'stan-js'

export const { actions, getState, reset, effect, useStore, useStoreEffect } = createStore({
    count: 0,
    name: 'John',
    notifications: [] as Array<Notification>
})

actions

Object that contains all functions that allows for updating the store's state

Action name is generated automatically based on given key to the store count -> setCount

You can pass the next state directly, or a function that calculates it from the previous state - similary to the useState hook

getState

Function that returns current state of the store

const { count } = getState()

console.log(count)

reset

Function that resets store state to the initial values

You can either pass all of the keys that you want to be reset, or if you won't pass any key WHOLE store will be reseted.

reset('count')
// Only count value will be reseted

reset('name', 'notifications')
// name and notifications will be reseted

reset()
// Whole store will be reseted

effect

Function that allows to subscribe to store's values change and react to them

It takes callback with current store's state that will be triggered on every store's value that you are using

const dispose = effect(({ count }) => {
    console.log(count)
})

If you won't pass any key to the dependencies it will trigger only once at the start - similarly to the useEffect hook

useStore

React's hook that allows to access store's values and to update them

It ONLY rerenders the component if the values that we access have changed

const { count, setCount, setName } = useStore()

console.log(count)

setCount(prev => prev + 1) // Component will rerender
setName('Anna') // Component won't rerender because it doesn't subscribe to name

useStoreEffect

React's hook that uses effect under the hood

You should use it inside React components, and in the other places feel free to use effect

useStoreEffect(({ count }) => {
    console.log(count)
})

Synchronizer

Synchronizer is an util that allows you to synchronize store with something external like localStorage, database, device storage (MMKV, AsyncStorage) etc.

type Synchronizer<T> = {
    value: T,
    subscribe: (update: (value: T) => void, key: string) => void,
    // If synchronizer doesn't have data that matches passed key, it should throw
    getSnapshot: (key: string) => T | Promise<T>,
    update: (value: T, key: string) => void
}

There is already implementation for localStorage and react-native-mmkv.

import { storage } from 'stan-js/storage' // localStorage

import { mmkvStorage } from 'stan-js/mmkv' // react-native-mmkv

Both storage and mmkvStorage takes two parameters - first is initial value, and the second one which is optional is options object with key (if the key isn't passed stan-js will pass key from the store), serialize and deserialize functions.

For react-native you need to install react-native-mmkv and if you are using react-native older than 0.72 you need to add this to your metro.config.js

unstable_enablePackageExports: true,

Read more about it here

If you want to store more complex objects that aren't supported by JSON you can either write your own storage synchronizer or pass custom serialize and deserialize functions to the options parameter. For example, you can use superjson package:

import { createStore } from 'stan-js'
import { storage } from 'stan-js/storage'
import superjson from 'superjson'

const { useStore } = createStore({
    user: storage(new Set(), {
        serialize: superjson.stringify,
        deserialize: superjson.deserialize
    })
})

Scoped store

If your app is SSR or for example you just want to have the same store shape but keep different values for different routes you can use scoped store

It returns:

  • StoreProvider - Provider that passes scoped store down to the React's tree
  • withStore - HOC that passes scoped store down to the React's tree
  • useScopedStore - React hook used to access scoped store
import { createScopedStore } from 'stan-js'

export const { StoreProvider, useScopedStore, withStore } = createScopedStore({
    count: 0,
})

Examples

SSR scoped store:

import { createScopedStore } from 'stan-js'

export const { StoreProvider, useScopedStore } = createScopedStore({
    count: 0,
    name: 'John'
})
// SSR Layout

<Layout>
    <StoreProvider initialValue={{ name: await db.getUser().name }}>
        {children}
    </StoreProvider>
</Layout>
// Some client component inside layout

const scopedStore = useScopedStore()
const { name } = scopedStore.useStore()

return (
    <h1>
        Hello {name}
    </h1>
)

Scoped store with regular routing

import { createScopedStore } from 'stan-js'

export const { StoreProvider, useScopedStore } = createScopedStore({
    count: 0,
    name: 'John'
})
const ProfileScreen = withStore(() => {
    // Component body...
})
// Some component inside ProfileScreen

const scopedStore = useScopedStore()
const { name } = scopedStore.useStore()

return (
    <h1>
        Hello {name}
    </h1>
)

Syncing values using synchronizer

localStorage
import { createStore } from 'stan-js'
import { storage } from 'stan-js/storage'

const { useStore } = createStore({
    counter: storage(0, { storageKey: 'counter-key' }), // number
    user: storage<string>(), // string | undefined
    cart: [] as Array<CartItem>
})
react-native-mmkv
import { createStore } from 'stan-js'
import { mmkvStorage } from 'stan-js/mmkv'

const { useStore } = createStore({
    counter: mmkvStorage(0, { storageKey: 'counter-key' }), // number
    user: mmkvStorage<string>(), // string | undefined
    cart: [] as Array<CartItem>
})