-
A lightweight, zero-dependency signals library for reactive JavaScript applications.
-
install
hmmfirst.hmm i @minejs/signals
-
import { signal, effect, computed, batch } from '@minejs/signals'
-
// Create a signal const count = signal(0) // Read value console.log(count()) // 0 // Update value count.set(5) console.log(count()) // 5 // Update with function count.update(n => n + 1) console.log(count()) // 6
-
const name = signal('John') // Effect runs automatically when dependencies change effect(() => { console.log('Hello,', name()) }) // Logs: "Hello, John" name.set('Jane') // Logs: "Hello, Jane"
-
const firstName = signal('John') const lastName = signal('Doe') // Computed value updates automatically const fullName = computed(() => { return `${firstName()} ${lastName()}` }) console.log(fullName()) // "John Doe" firstName.set('Jane') console.log(fullName()) // "Jane Doe"
-
const a = signal(0) const b = signal(0) effect(() => { console.log('Sum:', a() + b()) }) // Logs: "Sum: 0" // Without batch: effect runs twice a.set(1) // Logs: "Sum: 1" b.set(2) // Logs: "Sum: 3" // With batch: effect runs once batch(() => { a.set(10) b.set(20) }) // Logs: "Sum: 30" (only once!)
-
-
-
-
Create a reactive signal.
const count = signal(0) count() // Read: 0 count.set(5) // Write: 5 count.update(n => n + 1) // Update: 6 count.peek() // Read without tracking: 6
-
Run code automatically when dependencies change.
const count = signal(0) // Effect with cleanup const dispose = effect(() => { console.log('Count:', count()) // Optional cleanup function return () => { console.log('Cleaning up...') } }) // Stop the effect dispose()
-
Create a derived signal (memoized).
const count = signal(0) const doubled = computed(() => count() * 2) console.log(doubled()) // 0 count.set(5) console.log(doubled()) // 10
-
Batch multiple updates into one.
const a = signal(0) const b = signal(0) batch(() => { a.set(1) b.set(2) // Effects run only once here })
-
Read signals without tracking dependencies.
const count = signal(0) effect(() => { const value = untrack(() => count()) // count() is read but NOT tracked console.log(value) }) count.set(1) // Effect does NOT run
-
Run effect only when specific signal changes.
const count = signal(0) const other = signal('hello') on(count, (value, prevValue) => { console.log(`Changed from ${prevValue} to ${value}`) other() // Can read but won't track }) other.set('world') // Does NOT trigger count.set(1) // DOES trigger
-
Create an object of signals.
const state = store({ count : 0, name : 'John' }) state.count() // 0 state.name() // 'John' state.count.set(5) state.name.set('Jane')
-
Create a disposal scope for effects.
root((dispose) => { effect(() => { // ... effects ... }) // Clean up all effects at once dispose() })
-
Memoize expensive computations.
const expensiveComputation = memo(() => { // This computation runs only once and returns cached value return Math.sqrt(16) }) const result1 = expensiveComputation() // Computes const result2 = expensiveComputation() // Returns cached value
-
Subscribe to signal changes manually.
const count = signal(0) const unsubscribe = count.subscribe(() => { console.log('Signal changed!') }) count.set(1) // Logs: "Signal changed!" unsubscribe() // Stop listening
-
-
-
import { signal, effect } from '@minejs/signals' function Counter() { const count = signal(0) const button = document.createElement('button') effect(() => { button.textContent = `Count: ${count()}` }) button.onclick = () => count.update(n => n + 1) return button }
-
import { signal, computed } from '@minejs/signals' interface Todo { id : number text : string done : boolean } const todos = signal<Todo[]>([]) const filter = signal<'all' | 'active' | 'completed'>('all') const filteredTodos = computed(() => { const f = filter() const t = todos() if (f === 'active') return t.filter(todo => !todo.done) if (f === 'completed') return t.filter(todo => todo.done) return t }) const activeTodoCount = computed(() => { return todos().filter(t => !t.done).length }) // Actions function addTodo(text: string) { todos.update(list => [ ...list, { id: Date.now(), text, done: false } ]) } function toggleTodo(id: number) { todos.update(list => list.map(todo => todo.id === id ? { ...todo, done: !todo.done } : todo ) ) }
-
import { signal, computed } from '@crux/signals' const email = signal('') const password = signal('') const isEmailValid = computed(() => { return email().includes('@') && email().length > 3 }) const isPasswordValid = computed(() => { return password().length >= 8 }) const canSubmit = computed(() => { return isEmailValid() && isPasswordValid() }) effect(() => { const button = document.querySelector('#submit') button.disabled = !canSubmit() })
-
import { signal, effect } from '@minejs/signals' const userId = signal<number | null>(null) const userData = signal<any>(null) const loading = signal(false) effect(async () => { const id = userId() if (!id) return loading.set(true) try { const response = await fetch(`/api/users/${id}`) const data = await response.json() userData.set(data) } finally { loading.set(false) } }) // Fetch user when ID changes userId.set(123)
-
-
Notifications
You must be signed in to change notification settings - Fork 0
A lightweight, zero-dependency signals library for reactive JavaScript applications.
License
minejs-org/signals
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
Repository files navigation
About
A lightweight, zero-dependency signals library for reactive JavaScript applications.

