Skip to content

NeaByteLab/Stateless-JS

Repository files navigation

Stateless JS License: MIT Node.js CI

A universal and lightweight state management library with a clean, stateless API.

πŸ“¦ Installation

# Universal
npm install @neabyte/stateless-js

# Angular (Automatic Reactivity)
npm install @neabyte/stateless-js @angular/core

# React (Automatic Re-renders)
npm install @neabyte/stateless-js react

# Svelte (Automatic Reactivity)
npm install @neabyte/stateless-js svelte

# Vue (Automatic Reactivity)
npm install @neabyte/stateless-js vue

πŸ”§ Module Support

πŸ“‹ NPM

// CommonJS
const { createStateless, useStateless } = require('@neabyte/stateless-js')

// ESM (ES Modules)
import { createStateless, useStateless } from '@neabyte/stateless-js'

// Angular (Automatic Reactivity)
import { StatelessService, useAngularStateless } from '@neabyte/stateless-js/angular'

// React (Automatic Re-renders)
import { useReactStateless } from '@neabyte/stateless-js/react'

// Svelte (Automatic Reactivity)
import { useSvelteStateless } from '@neabyte/stateless-js/svelte'

// Vue (Automatic Reactivity)
import { useVueStateless } from '@neabyte/stateless-js/vue'

// TypeScript types
import type { StateSetter, StateValue } from '@neabyte/stateless-js'

🌐 Browser

// CDN
<script src="https://unpkg.com/@neabyte/stateless-js/dist/index.mjs"></script>

// jsDelivr ESM
<script type="module">
  import { createStateless, useStateless } from 'https://cdn.jsdelivr.net/npm/@neabyte/stateless-js/dist/index.mjs'
</script>

πŸš€ Usage

πŸ“ Universal Usage

import { createStateless, useStateless } from '@neabyte/stateless-js'

// Create a new state
const [setCount, count] = createStateless('counter', 0)
console.log(count.value) // 0

// Update state
setCount(current => current + 1)
console.log(count.value) // 1

πŸ”· Angular Usage

import { StatelessService, useAngularStateless } from '@neabyte/stateless-js/angular'

// Using the service approach
@Injectable({ providedIn: 'root' })
export class MyService {
  constructor(private stateless: StatelessService) {}
  counterState = this.stateless.useAngularStateless('counter', 0)
  count = this.counterState[1]
  setCount = this.counterState[0]
}

// Using the standalone function approach
export class MyComponent {
  counterState = useAngularStateless('counter', 0)
  count = this.counterState[1]
  setCount = this.counterState[0]
  increment = () => this.setCount(current => current + 1)
}
// In your Angular component
@Component({
  template: `
    <div>
      <p>Count: {{ count() }}</p>
      <button (click)="increment()">Increment</button>
    </div>
  `
})
export class CounterComponent {
  counterState = useAngularStateless('counter', 0)
  count = this.counterState[1]
  setCount = this.counterState[0]
  increment = () => this.setCount(current => current + 1)
}

βš›οΈ React Usage

import { useReactStateless } from '@neabyte/stateless-js/react'

function Counter() {
  const [setCount, count] = useReactStateless('counter', 0)
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(c => c + 1)}>
        Increment
      </button>
    </div>
  )
}

🟠 Svelte Usage

import { useSvelteStateless } from '@neabyte/stateless-js/svelte'

// In your Svelte component
const [setCount, count] = useSvelteStateless('counter', 0)
const increment = () => setCount(current => current + 1)
<!-- In your .svelte file -->
<script>
  import { useSvelteStateless } from '@neabyte/stateless-js/svelte'
  const [setCount, count] = useSvelteStateless('counter', 0)
  const increment = () => setCount(current => current + 1)
</script>
<div>
  <p>Count: {$count}</p>
  <button on:click={increment}>Increment</button>
</div>

🟒 Vue Usage

import { useVueStateless } from '@neabyte/stateless-js/vue'

export default {
  name: 'Counter',
  setup() {
    const [setCount, countValue] = useVueStateless('counter', 0)
    const count = computed(() => countValue.value)
    const increment = () => {
      setCount(current => current + 1)
    }
    return {
      count,
      increment
    }
  }
}

πŸ”„ State Sharing (Universal, React & Vue)

// Universal
const [setUser, user] = createStateless('user', { name: 'John', age: 25 })
const [setUser2, user2] = useStateless('user', { name: 'Default', age: 0 })
console.log(user.value === user2.value) // true - same state

// React - Multiple components share the same state
function UserProfile() {
  const [setUser, user] = useReactStateless('user', { name: 'John', age: 25 })
  return <div>Name: {user.name}, Age: {user.age}</div>
}

function UserEditor() {
  const [setUser, user] = useReactStateless('user', { name: 'Default', age: 0 })
  return <button onClick={() => setUser(u => ({ ...u, age: u.age + 1 }))}>
    Increase Age
  </button>
}

// Vue - Multiple components share the same state
export default {
  name: 'UserProfile',
  setup() {
    const [setUser, userValue] = useVueStateless('user', { name: 'John', age: 25 })
    const user = computed(() => userValue.value)
    return { user }
  }
}

export default {
  name: 'UserEditor',
  setup() {
    const [setUser, userValue] = useVueStateless('user', { name: 'Default', age: 0 })
    const user = computed(() => userValue.value)
    const increaseAge = () => {
      setUser(u => ({ ...u, age: u.age + 1 }))
    }
    return { user, increaseAge }
  }
}

🎯 Different Data Types

// Numbers
const [setNum, num] = createStateless('number', 42)

// Strings
const [setStr, str] = createStateless('string', 'hello')

// Objects
const [setObj, obj] = createStateless('object', { count: 0, items: [] })

// Arrays
const [setArr, arr] = createStateless('array', [1, 2, 3])

// Special values
const [setNaN, nan] = createStateless('nan', NaN)
const [setInf, inf] = createStateless('infinity', Infinity)
const [setSymbol, sym] = createStateless('symbol', Symbol('test'))
const [setBigInt, big] = createStateless('bigint', BigInt(123))

πŸ”§ Object Updates

// Universal
const [setData, data] = createStateless('data', { count: 0, items: [] })

// React
const [setData, data] = useReactStateless('react-data', { count: 0, items: [] })

// Vue
const [setData, dataRef] = useVueStateless('vue-data', { count: 0, items: [] })
const data = computed(() => dataRef.value)

// Immutable updates work the same way
setData(current => ({ ...current, count: current.count + 1 }))
setData(current => ({ ...current, items: [...current.items, 'new item'] }))

🧹 Memory Management

const [setTemp, temp] = createStateless('temporary', 'data')

// Use the state...
console.log(temp.value)

// Clean up when done
temp.dispose()

// Accessing disposed state throws error
try {
  console.log(temp.value) // Error: State has been disposed
} catch (error) {
  console.log(error.message)
}

πŸ“š API Reference

Universal Functions

createStateless<T>(id: StateId, initialState: T): StatelessReturn<T>

Creates a new state with the given ID and initial value.

  • id: Unique identifier (string or number)
  • initialState: Initial value for the state
  • Returns: Tuple [StateSetter<T>, StateValue<T>]

useStateless<T>(id: StateId, fallbackState: T): StatelessReturn<T>

Uses existing state if it exists, otherwise creates a new one.

  • id: Unique identifier (string or number)
  • fallbackState: Value to use if state doesn't exist
  • Returns: Tuple [StateSetter<T>, StateValue<T>]

Angular Functions

StatelessService.useAngularStateless<T>(id: StateId, initialValue: T): [StateSetter<T>, Signal<T>]

Angular service method that provides automatic reactivity using Angular signals.

  • id: Unique identifier (string or number)
  • initialValue: Initial value for the state
  • Returns: Tuple [StateSetter<T>, Signal<T>] (Angular signal for reactivity)

useAngularStateless<T>(id: StateId, initialValue: T): [StateSetter<T>, Signal<T>]

Standalone Angular function that provides automatic reactivity using Angular signals.

  • id: Unique identifier (string or number)
  • initialValue: Initial value for the state
  • Returns: Tuple [StateSetter<T>, Signal<T>] (Angular signal for reactivity)

React Functions

useReactStateless<T>(id: StateId, initialValue: T): [StateSetter<T>, T]

React hook that provides automatic re-renders when state changes.

  • id: Unique identifier (string or number)
  • initialValue: Initial value for the state
  • Returns: Tuple [StateSetter<T>, T] (direct value, not StateValue object)

Svelte Functions

useSvelteStateless<T>(id: StateId, initialValue: T): [StateSetter<T>, Writable<T>]

Svelte function that provides automatic reactivity using Svelte stores.

  • id: Unique identifier (string or number)
  • initialValue: Initial value for the state
  • Returns: Tuple [StateSetter<T>, Writable<T>] (Svelte writable store for reactivity)

Vue Functions

useVueStateless<T>(id: StateId, initialValue: T): [StateSetter<T>, Ref<T>]

Vue composable that provides automatic reactivity when state changes.

  • id: Unique identifier (string or number)
  • initialValue: Initial value for the state
  • Returns: Tuple [StateSetter<T>, Ref<T>] (reactive ref for Vue reactivity)

Types

StateSetter<T>

Function to update state: (updater: StateUpdater<T>) => void

StateValue<T>

Object with:

  • value: Current state value (readonly)
  • dispose(): Method to clean up the state

πŸ“„ License

This project is licensed under the MIT license. See the LICENSE file for more info.