Independent State Management

In this section, we explore how to implement independent state management using Nanostores across different frameworks within an Astro project. Independent state means that each component maintains its own isolated state, demonstrating how Nanostores can be used for component-specific state management.

We'll showcase this concept using two common UI patterns: counters and forms. Each example is implemented in Astro, Svelte, React, and Vue, allowing you to compare how independent state management works across these frameworks.

Independent State Counters

These counters demonstrate how each framework can maintain its own independent count state using Nanostores. Notice how updating one counter doesn't affect the others.

Astro Counter

Independent Astro Counter

0

Svelte Counter

Independent Svelte Counter

0

React Counter

Independent React Counter

0

Vue Counter

Independent Vue Counter

0

Counter Code Examples

import { atom } from 'nanostores'

export const $astroCount = atom(0)
export const $svelteCount = atom(0)
export const $reactCount = atom(0)
export const $vueCount = atom(0)

export function increment(store) {
	store.set(store.get() + 1)
}

export function decrement(store) {
	if (store.get() > 0) store.set(store.get() - 1)
}
---
import { $astroCount, increment, decrement } from '../stores/counterStore'
---

<div>
	<button id="dec">-</button>
	<span>{$astroCount.get()}</span>
	<button id="inc">+</button>
</div>

<script>
	import { $astroCount, increment, decrement } from '../stores/counterStore'

	document.getElementById('dec')?.addEventListener('click', () => decrement($astroCount))
	document.getElementById('inc')?.addEventListener('click', () => increment($astroCount))

	$astroCount.subscribe(value => {
		document.querySelector('span').textContent = value
	})
</script>
<script>
import { $svelteCount, increment, decrement } from '../stores/counterStore'
</script>

<div>
	<button onclick={() => decrement($svelteCount)}>-</button>
	<span>{$svelteCount}</span>
	<button onclick={() => increment($svelteCount)}>+</button>
</div>
import { useStore } from '@nanostores/react'
import { $reactCount, increment, decrement } from '../stores/counterStore'

export function ReactCounter() {
	const count = useStore($reactCount)

	return (
		<div>
			<button onClick={() => decrement($reactCount)}>-</button>
			<span>{count}</span>
			<button onClick={() => increment($reactCount)}>+</button>
		</div>
	)
}
<template>
	<div>
		<button @click="decrement($vueCount)">-</button>
		<span>{{ count }}</span>
		<button @click="increment($vueCount)">+</button>
	</div>
</template>

<script setup>
import { useStore } from '@nanostores/vue'
import { $vueCount, increment, decrement } from '../stores/counterStore'

const count = useStore($vueCount)
</script>

Independent State Forms

These forms demonstrate how complex state (multiple form fields) can be managed independently for each framework using Nanostores. Each form maintains its own state without affecting the others.

Astro Form

Independent Astro Form

Svelte Form

Independent Svelte Form

React Form

Independent React Form

Vue Form

Independent Vue Form

Form Code Examples

import { map } from 'nanostores'

export const $astroForm = map({ name: '', email: '' })
export const $svelteForm = map({ name: '', email: '' })
export const $reactForm = map({ name: '', email: '' })
export const $vueForm = map({ name: '', email: '' })
---
import { $astroForm } from '../stores/formStore'
---

<form id="astroForm">
	<input type="text" id="name" value={$astroForm.get().name} />
	<input type="email" id="email" value={$astroForm.get().email} />
</form>

<script>
	import { $astroForm } from '../stores/formStore'

	const form = document.getElementById('astroForm')
	form?.addEventListener('input', (e) => {
		const target = e.target as HTMLInputElement
		$astroForm.setKey(target.id, target.value)
	})
</script>
<script>
import { $svelteForm } from '../stores/formStore'

function handleInput(event) {
	const { name, value } = event.target
	$svelteForm.setKey(name, value)
}
</script>

<form>
	<input type="text" name="name" value={$svelteForm.name} oninput={handleInput} />
	<input type="email" name="email" value={$svelteForm.email} oninput={handleInput} />
</form>
import { useStore } from '@nanostores/react'
import { $reactForm } from '../stores/formStore'

export function ReactForm() {
	const form = useStore($reactForm)

	return (
		<form>
			<input
				type="text"
				value={form.name}
				onChange={(e) => $reactForm.setKey('name', e.target.value)}
			/>
			<input
				type="email"
				value={form.email}
				onChange={(e) => $reactForm.setKey('email', e.target.value)}
			/>
		</form>
	)
}
<template>
	<form>
		<input
			type="text"
			:value="form.name"
			@input="(e) => $vueForm.setKey('name', e.target.value)"
		/>
		<input
			type="email"
			:value="form.email"
			@input="(e) => $vueForm.setKey('email', e.target.value)"
		/>
	</form>
</template>

<script setup>
import { useStore } from '@nanostores/vue'
import { $vueForm } from '../stores/formStore'

const form = useStore($vueForm)
</script>

I'm persistent

0