Introducción
Bienvenido a la primera parte de la serie “Nanostores in Astro: A Multi-Framework Adventure”. Si has estado lidiando con el manejo de estado en tus proyectos Astro de multi-framework, tendrás un regalo. Hoy, exploraremos Nanostores, una solución de manejo de estado ligera que se integra sin problemas con la arquitectura de islas de componentes de Astro.
En este artículo, adentraremos cómo Nanostores puede simplificar el manejo de estado a través de componentes de Astro, React, Vue y Svelte. Cubriremos el estado independiente, el estado compartido y presentaremos el manejo de estado persistente. ¡Empecemos esta aventura para simplificar el manejo de estado en nuestros proyectos Astro!
Resumen breve
Si estás ansioso por sumergirte directamente, aquí tienes un resumen rápido y algunos enlaces esenciales:
-
Este artículo explora Nanostores para el manejo de estado en proyectos Astro de multi-framework.
-
Cubrimos el manejo de estado independiente, compartido y persistente a través de Astro, React, Vue y Svelte.
-
Revísala estas herramientas:
¡No dude en explorar la demostración y el código junto con este artículo para una experiencia de aprendizaje práctico!
Comprensión de Nanostores
¿Qué son los Nanostores?
Nanostores es una biblioteca de gestión de estado minimalista diseñada pensando en la agnosticidad de frameworks. Proporciona una API directa para crear y administrar pequeños trozos de estado atómicos, que pueden ser fácilmente compartidos y actualizados en diferentes partes de tu aplicación.
¿Por qué usar Nanostores en Astro?
-
Lightweight y rápido: Nanostores es increíblemente pequeño (Entre 265 y 814 bytes), garantizando que no empeorará el tamaño de tu paquete.
-
Agnóstico respecto a los frameworks: Perfecto para el ecosistema multi-framework de Astro. Se integra sin problemas con React, Vue, Svelte, Solid y JavaScript puro.
-
API Simple: No hay configuración compleja o boilerplate. Es directo e intuitivo de usar.
-
Complementario a las Islas de Componentes de Astro: Nanostores refuerza la arquitectura de islas de Astro, permitiendo una eficiente gestión de estado a través de componentes interactivos aislados.
Conceptos Básicos
Nanostores se basa en tres conceptos principales:
-
Atomos: Almacenes simples que contienen un solo valor.
-
Mapas: Almacenes que contienen objetos con múltiples propiedades.
-
Almacenes Computados: Almacenes derivados que calculan su valor basado en otros almacenes.
Veamos un ejemplo rápido:
import { atom, map, computed } from 'nanostores'
// Atom
const count = atom(0)
// Map
const user = map({ name: 'Astro Fan', isLoggedIn: false })
// Computed Store
const greeting = computed([user], (user) =>
user.isLoggedIn ? `Welcome back, ${user.name}!` : 'Hello, guest!'
)
En este fragmento, hemos creado un atomo para un contador, un mapa para los datos del usuario y un almacén computado para un saludo dinámico. ¿Sencillo, verdad?
Configurar Nanostores en un proyecto Astro
Comenzar con Nanostores en tu proyecto Astro es directo. Aquí cómo hacerlo:
- Primero, instala Nanostores y sus integraciones con el framework:
# Using npm
npm install nanostores
npm install @nanostores/react # For React
npm install @nanostores/vue # For Vue
# Using yarn
yarn add nanostores
yarn add @nanostores/react # For React
yarn add @nanostores/vue # For Vue
# Using pnpm
pnpm add nanostores
pnpm add @nanostores/react # For React
pnpm add @nanostores/vue # For Vue
# Note: Svelte doesn't require a separate integration
- Crea un nuevo archivo para tus almacenes, digamos
src/stores/counterStore.js
:
import { atom } from 'nanostores'
export const count = atom(0)
export function increment() {
count.set(count.get() + 1)
}
export function decrement() {
count.set(count.get() - 1)
}
- Ahora puedes usar este almacén en cualquiera de tus componentes. Aquí tienes un ejemplo rápido en un componente de Astro:
---
import { count, increment, decrement } from '../stores/counterStore'
---
<div>
<button onclick={decrement}>-</button>
<span>{count.get()}</span>
<button onclick={increment}>+</button>
</div>
<script>
import { count } from '../stores/counterStore'
count.subscribe(value => {
document.querySelector('span').textContent = value
})
</script>
Y allí tienes! Acabas de configurar un Nanostore en tu proyecto Astro.
Gestión de Estado Independiente
En proyectos Astro con multi-framework, es posible que desee gestionar el estado de forma independiente dentro de componentes de diferentes frameworks. Nanostores hace que esta tarea sea transparente. Vamos a explorar cómo implementar la gestión de estado independiente entre componentes de React, Vue, Svelte y Astro.
Ejemplo de Contador
Implementaremos un contador simple en cada framework para demostrar la gestión de estado independiente.
Primero, vamos a crear nuestro contador independiente de estado:
// src/stores/independentCounterStore.js
import { atom } from 'nanostores'
export const reactCount = atom(0)
export const vueCount = atom(0)
export const svelteCount = atom(0)
export const astroCount = atom(0)
export function increment(store) {
store.set(store.get() + 1)
}
export function decrement(store) {
store.set(store.get() - 1)
}
Ahora, implementaremos este contador en cada framework:
Contador de React
// src/components/ReactCounter.jsx
import { useStore } from '@nanostores/react'
import { reactCount, increment, decrement } from '../stores/independentCounterStore'
export function ReactCounter() {
const count = useStore(reactCount)
return (
<div>
<button onClick={() => decrement(reactCount)}>-</button>
<span>{count}</span>
<button onClick={() => increment(reactCount)}>+</button>
</div>
)
}
Contador de Vue
<!-- src/components/VueCounter.vue -->
<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/independentCounterStore'
const count = useStore(vueCount)
</script>
Contador de Svelte
<!-- src/components/SvelteCounter.svelte -->
<script>
import { svelteCount, increment, decrement } from '../stores/independentCounterStore'
</script>
<div>
<button on:click={() => decrement(svelteCount)}>-</button>
<span>{$svelteCount}</span>
<button on:click={() => increment(svelteCount)}>+</button>
</div>
Contador de Astro
---
import { astroCount, increment, decrement } from '../stores/independentCounterStore'
---
<div>
<button id="decrement">-</button>
<span id="count">{astroCount.get()}</span>
<button id="increment">+</button>
</div>
<script>
import { astroCount, increment, decrement } from '../stores/independentCounterStore'
document.getElementById('decrement').addEventListener('click', () => decrement(astroCount))
document.getElementById('increment').addEventListener('click', () => increment(astroCount))
astroCount.subscribe(value => {
document.getElementById('count').textContent = value
})
</script>
Como puede ver, cada componente del framework mantiene su propio contador de estado independiente utilizando Nanostores. Este enfoque permite la gestión de estado aislado dentro de cada componente, sin importar el framework utilizado.
Estado Compartido Entre Frameworks
Ahora, exploraremos cómo Nanostores permite la gestión de estado compartido entre componentes de diferentes frameworks. Esto es particularmente útil cuando se necesita sincronizar estado entre diferentes partes de su aplicación.
Ejemplo de Contador Compartido
Crearemos un contador compartido que pueda actualizarse y mostrarse entre componentes de React, Vue, Svelte y Astro.
Primero, vamos a crear nuestro almacén de contador compartido:
// src/stores/sharedCounterStore.js
import { atom } from 'nanostores'
export const sharedCount = atom(0)
export function increment() {
sharedCount.set(sharedCount.get() + 1)
}
export function decrement() {
sharedCount.set(sharedCount.get() - 1)
}
Ahora, implementaremos componentes en cada framework que utilicen este estado compartido:
Contador Compartido de React
// src/components/ReactSharedCounter.jsx
import { useStore } from '@nanostores/react'
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
export function ReactSharedCounter() {
const count = useStore(sharedCount)
return (
<div>
<h2>React Shared Counter</h2>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
)
}
Contador Compartido de Vue
<!-- src/components/VueSharedCounter.vue -->
<template>
<div>
<h2>Vue Shared Counter</h2>
<button @click="decrement">-</button>
<span>{{ count }}</span>
<button @click="increment">+</button>
</div>
</template>
<script setup>
import { useStore } from '@nanostores/vue'
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
const count = useStore(sharedCount)
</script>
Contador Compartido de Svelte
<!-- src/components/SvelteSharedCounter.svelte -->
<script>
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
</script>
<div>
<h2>Svelte Shared Counter</h2>
<button on:click={decrement}>-</button>
<span>{$sharedCount}</span>
<button on:click={increment}>+</button>
</div>
Contador Compartido de Astro
---
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
---
<div>
<h2>Astro Shared Counter</h2>
<button id="shared-decrement">-</button>
<span id="shared-count">{sharedCount.get()}</span>
<button id="shared-increment">+</button>
</div>
<script>
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
document.getElementById('shared-decrement').addEventListener('click', decrement)
document.getElementById('shared-increment').addEventListener('click', increment)
sharedCount.subscribe(value => {
document.getElementById('shared-count').textContent = value
})
</script>
Con este configurado, todos estos componentes compartirán el mismo estado del contador. Incrementar o decrementar el contador en cualquier componente actualizará el valor en todos los componentes, sin importar el marco utilizado.
Gestión de Estado Persistente
Mientras que estados independientes y compartidos son poderosos, a veces necesitamos que nuestro estado persista a través de recargas de página o incluso sesiones de navegador. Aquí es donde entra en juego @nanostores/persistent
. Exploremos cómo implementar un estado persistente en nuestro proyecto Astro.
Configurando el Estado Persistente
Primero, necesitamos instalar el complemento de estado persistente para Nanostores:
# Using npm
npm install @nanostores/persistent
# Using yarn
yarn add @nanostores/persistent
# Using pnpm
pnpm add @nanostores/persistent
Ahora, vamos a crear un contador persistente que mantendrá su valor incluso cuando se recargue la página:
// src/stores/persistentCounterStore.js
import { persistentAtom } from '@nanostores/persistent'
export const persistentCount = persistentAtom('persistentCount', 0)
export function increment() {
persistentCount.set(persistentCount.get() + 1)
}
export function decrement() {
persistentCount.set(persistentCount.get() - 1)
}
export function reset() {
persistentCount.set(0)
}
En este ejemplo, ‘persistentCount’ es la clave usada para almacenar el valor en localStorage, y 0 es el valor inicial.
Ejemplo de Contador Persistente Multimarca
Implementemos un contador persistente usando componentes de diferentes marcos. Este contador mantendrá su valor a través de recargas de página y estará accesible desde cualquier marco.
Contador Persistente de React (Incremento)
// src/components/ReactPersistentIncrement.jsx
import { useStore } from '@nanostores/react'
import { persistentCount, increment } from '../stores/persistentCounterStore'
export function ReactPersistentIncrement() {
const count = useStore(persistentCount)
return (
<button onClick={increment}>
React Increment: {count}
</button>
)
}
Contador Persistente de Vue (Decremento)
<!-- src/components/VuePersistentDecrement.vue -->
<template>
<button @click="decrement">
Vue Decrement: {{ count }}
</button>
</template>
<script setup>
import { useStore } from '@nanostores/vue'
import { persistentCount, decrement } from '../stores/persistentCounterStore'
const count = useStore(persistentCount)
</script>
Contador Persistente de Svelte (Visualización)
<!-- src/components/SveltePersistentDisplay.svelte -->
<script>
import { persistentCount } from '../stores/persistentCounterStore'
</script>
<div>
Svelte Display: {$persistentCount}
</div>
Contador Persistente de Astro (Restablecer)
---
import { reset } from '../stores/persistentCounterStore'
---
<button id="reset-button">Astro Reset</button>
<script>
import { persistentCount, reset } from '../stores/persistentCounterStore'
document.getElementById('reset-button').addEventListener('click', reset)
persistentCount.subscribe(value => {
console.log('Persistent count updated:', value)
})
</script>
Ahora, puedes usar estos componentes juntos en una página de Astro:
---
import ReactPersistentIncrement from '../components/ReactPersistentIncrement'
import VuePersistentDecrement from '../components/VuePersistentDecrement.vue'
import SveltePersistentDisplay from '../components/SveltePersistentDisplay.svelte'
---
<div>
<h2>Persistent Counter Across Frameworks</h2>
<ReactPersistentIncrement client:load />
<VuePersistentDecrement client:load />
<SveltePersistentDisplay client:load />
<button id="reset-button">Astro Reset</button>
</div>
<script>
import { persistentCount, reset } from '../stores/persistentCounterStore'
document.getElementById('reset-button').addEventListener('click', reset)
persistentCount.subscribe(value => {
console.log('Persistent count updated:', value)
})
</script>
Este conjunto muestra un contador persistente en el que:
-
React maneja el incremento
-
Vue maneja el decremento
-
Svelte muestra el conteo actual
-
Astro proporciona un botón de reiniciar
El valor del contador persistirá entre recargas de página, mostrando el poder de @nanostores/persistent
para mantener el estado.
Casos de Uso para Estado Persistente
El estado persistente es particularmente útil para:
-
Preferencias de usuario (p. ej., ajustes de tema, opciones de idioma)
-
Formularios parcialmente completados (para evitar la pérdida de datos por recarga accidental de página)
-
Token de autenticación (para mantener las sesiones de usuario)
-
Caché local de datos accedidos con frecuencia
Al aprovechar @nanostores/persistent
, puedes mejorar la experiencia del usuario manteniendo datos de estado importantes entre cargas de página y sesiones del navegador.
Mejores Prácticas y Consejos
Al integrar Nanostores en tus proyectos Astro, tienes en mente estas mejores prácticas y consejos para aprovechar al máximo esta solución de gestión de estado ligera.
1. Elige el Tipo de Almacén Correcto
-
Use
atom
para estados simples de valor único. -
Utilice
map
para estados tipo objeto con múltiples propiedades. -
Utilice
computed
para estados derivados que dependen de otros almacenes. -
Utilice
persistentAtom
opersistentMap
cuando necesite que el estado persista a través de recargas de página.
2. Mantiene los Almacenes pequeños y enfocados
En lugar de crear almacenes grandes y monolíticos, prefiera almacenes pequeños y enfocados. Esta aproximación mejora la mantenibilidad y el rendimiento permitiendo actualizaciones más granulares.
// Prefer this:
const userProfile = map({ name: '', email: '' })
const userPreferences = map({ theme: 'light', language: 'en' })
// Over this:
const user = map({ name: '', email: '', theme: 'light', language: 'en' })
3. Use Almacenes Computados para Estados Derivados
Cuando tenga estados que dependan de otras partes de estado, use almacenes computados. Esto ayuda a mantener su estado DRY (Don’t Repeat Yourself) y asegura que el estado derivado siempre esté actualizado.
import { atom, computed } from 'nanostores'
const firstName = atom('John')
const lastName = atom('Doe')
const fullName = computed(
[firstName, lastName],
(first, last) => `${first} ${last}`
)
4. Aproveche TypeScript para la Seguridad de Tipos
Nanostores ofrece excelente soporte para TypeScript. Utilícelo para atrapar errores temprano y mejorar la experiencia del desarrollador.
import { atom } from 'nanostores'
interface User {
id: number
name: string
}
const currentUser = atom<User | null>(null)
5. Considerar el Rendimiento en Aplicaciones Grandes
Aunque Nanostores es ligero, tenga en mente el rendimiento en aplicaciones más grandes. Use la función batched
para agrupar múltiples actualizaciones de almacén juntas, reduciendo el número de re-renderizaciones.
import { atom, batched } from 'nanostores'
const count1 = atom(0)
const count2 = atom(0)
export const incrementBoth = batched(() => {
count1.set(count1.get() + 1)
count2.set(count2.get() + 1)
})
6. Mantén la Lógica Específica del Framework Separada
Cuando uses Nanostores en un proyecto Astro con varios frameworks, intenta mantener la lógica central del estado independiente del framework. Esto facilita compartir el estado entre diferentes componentes de frameworks.
// stores/themeStore.js
import { atom } from 'nanostores'
export const theme = atom('light')
export function toggleTheme() {
theme.set(theme.get() === 'light' ? 'dark' : 'light')
}
// React component
import { useStore } from '@nanostores/react'
import { theme, toggleTheme } from '../stores/themeStore'
function ThemeToggle() {
const currentTheme = useStore(theme)
return <button onClick={toggleTheme}>{currentTheme}</button>
}
7. Usa Tiendas Persistentes con Cautela
Aunque las tiendas persistentes son poderosas, úsalas con precaución. No todo el estado necesita persistir entre sesiones. El uso excesivo de tiendas persistentes puede generar comportamientos inesperados y posibles problemas de rendimiento.
8. Depuración de Nanostores
Para facilitar la depuración, puedes usar la función onMount
para registrar los cambios de estado:
import { atom, onMount } from 'nanostores'
const count = atom(0)
if (import.meta.env.DEV) {
onMount(count, () => {
count.listen((value) => {
console.log('Count changed:', value)
})
})
}
9. Limpieza de Suscripciones
Cuando utilices Nanostores en componentes que puedan desmontarse, asegúrate de limpiar las suscripciones para evitar pérdidas de memoria.
import { useEffect } from 'react'
import { count } from '../stores/countStore'
function Counter() {
useEffect(() => {
const unsubscribe = count.subscribe(() => {
// Do something
})
return unsubscribe
}, [])
// Rest of the component
}
Siguiendo estas mejores prácticas y consejos, podrás gestionar eficazmente el estado en tus proyectos Astro usando Nanostores, sin importar los frameworks que estés integrando.
Conclusión
Como hemos explorado a lo largo de este artículo, Nanostores ofrece una solución poderosa pero ligera para la gestión de estado en proyectos Astro, especialmente cuando se trabaja con múltiples frameworks. Recapitulemos los puntos clave:
-
Versatilidad: Nanostores se integra perfectamente con Astro, React, Vue, Svelte y Solid, lo que lo convierte en una opción ideal para proyectos con múltiples frameworks.
-
Simplicidad
: Con su API directo, Nanostores ofrece una pendiente de aprendizaje baja mientras que aún proporciona capabilidades robustas de gestión de estado.
- Flexibilidad: Desde almacenes atómicos simples hasta estados computados complejos e incluso almacenamiento persistente, Nanostores se adapta a una amplia gama de necesidades de gestión de estado.
- Rendimiento: Su naturaleza ligera garantiza que Nanostores no inflará su aplicación, manteniendo los beneficios de rendimiento de Astro.
- Mejores Prácticas: Al seguir las guías que hemos discutido, como mantener los almacenes pequeños y enfocados, aprovechar TypeScript y usar almacenes computados para estados derivados, puedes crear sistemas de gestión de estado eficientes y maintainables.
Nanostores destaca en la arquitectura de islas de componentes de Astro, permitiéndole administrar el estado a través de componentes interactivos aislados de manera eficiente. Tanto si estás construyendo una página web simple con algunos elementos interactivos como si estás trabajando en una aplicación web compleja con múltiples frameworks, Nanostores proporciona las herramientas necesarias para manejar el estado de manera efectiva.
Mientras continúas tu viaje con Astro y Nanostores, recuerda que la mejor manera de aprender es haciendo. Experimenta con diferentes tipos de tiendas, intenta implementar estado compartido entre frameworks y explora las posibilidades de almacenamiento persistente. Cada proyecto traerá nuevos retos y oportunidades para refinar tus habilidades en el manejo del estado.
Este atento al próximo artículo de nuestra serie “Nanostores en Astro: una aventura con multiples frameworks”, donde profundizaremos en aplicaciones prácticas y técnicas avanzadas para el manejo de estado en proyectos de Astro.
Recursos adicionales
Para profundizar tu comprensión de Nanostores y su uso en proyectos de Astro, revisa estos recursos valiosos:
- Repositorio de proyecto de demo de GitHub
- Sitio web de demostración en vivo
- Documentación de Astro sobre la compartición de estado
- Documentación oficial de Nanostores
- Repositorio persistente de GitHub de Nanostores
¡Buen código y que tus proyectos Astro sean siempre estados y de alto rendimiento!
Source:
https://meirjc.hashnode.dev/state-management-in-astro-a-deep-dive-into-nanostores