Introdução
Bem-vindo à primeira parte da série “Nanostores in Astro: A Multi-Framework Adventure”. Se você tiver lutado com o gerenciamento de estado em seus projetos Astro multi-framework, você vai adorar. Hoje, nós estaremos explorando Nanostores, uma solução de gerenciamento de estado leve que integra-se perfeitamente com a arquitetura de ilhas de componentes do Astro.
Neste artigo, nós abordaremos como os Nanostores podem simplificar o gerenciamento de estado entre componentes Astro, React, Vue e Svelte. Vamos cobrir estado independente, estado compartilhado e apresentar o gerenciamento de estado persistente. Vamos embarcar nesta jornada para simplificar o gerenciamento de estado em nossos projetos Astro.
Resumo rápido
Se você está ansioso por uma breve visão geral, aqui está um resumo rápido e alguns links essenciais:
-
Este artigo explora Nanostores para o gerenciamento de estado em projetos Astro multi-framework.
-
Nós abordamos o gerenciamento de estado independente, compartilhado e persistente entre Astro, React, Vue e Svelte.
-
Veja esses recursos:
Fique livre para explorar a demonstração e o código ao lado deste artigo para uma experiência de aprendizagem prática!
Entendendo as Nanostores
O que são as Nanostores?
Nanostores é uma biblioteca de gerenciamento de estado minimalista projetada com agnosticismo de framework em mente. Ela fornece uma API simples para criar e gerenciar pequenos pedaços de estado atômicos, que podem ser facilmente compartilhados e atualizados em diferentes partes do seu aplicativo.
Por que usar Nanostores em Astro?
-
Leve e Rápido: Nanostores é incrivelmente pequeno (Entre 265 e 814 bytes), garantindo que não vai encher o tamanho do pacote de seu aplicativo.
-
Agnóstico em Framework: Perfeito para o ecossistema multi-framework de Astro. Integra-se sem problemas com React, Vue, Svelte, Solid e JavaScript Vanilla.
-
API Simples: Sem configuração complexa ou boilerplate. É simples e intuitivo de usar.
-
Complementar aos Ilhas de Componentes de Astro: Nanostores melhora a arquitetura de ilhas de componentes do Astro, permitindo um gerenciamento eficiente de estado através de componentes interativos isolados.
Conceitos Básicos
Nanostores se baseia em três conceitos principais:
-
Átomos: Armazenamentos simples que contêm um valor único.
-
Mapas: Armazenamentos que mantêm objetos com várias propriedades.
-
Armazenamentos Computados: Armazenamentos derivados que calculam seu valor com base em outros armazenamentos.
Vamos olhar por um exemplo 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!'
)
Neste snippet, criamos um átomo para um contador, um mapa para dados do usuário e um armazenamento computado para um cumprimento dinâmico. Simples, não é?
Configurando Nanostores em um Projeto Astro
Iniciar com Nanostores em seu projeto Astro é fácil. Veja como fazer:
- Primeiro, instale Nanostores e suas integrações de 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
- Crie um novo arquivo para seus armazenamentos, 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)
}
- Agora você pode usar este armazenamento em qualquer componente dele. Aqui está um exemplo rápido em um componente 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>
E lá você tem! Você acabou de configurar um Nanostore em seu projeto Astro.
Gerenciamento de Estado independente
Em projetos Astro multi-framework, você pode querer gerenciar estados independentemente dentro de componentes de diferentes frameworks. Nanostores torna isso sinuoso. Vamos explorar como implementar o gerenciamento de estado independente entre componentes React, Vue, Svelte e Astro.
Exemplo de Contador
Nós implementaremos um simples contador em cada framework para demonstrar o gerenciamento de estado independente.
Primeiro, vamos criar nosso contador independente:
// 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)
}
Agora, implementemos este contador em cada framework:
Contador 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 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 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 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 você pode ver, cada componente de framework mantém seu próprio contador independente de estado usando Nanostores. Esta abordagem permite o gerenciamento de estado isolado dentro de cada componente, independentemente do framework usado.
Estado Compartilhado Entre Frameworks
Agora, vamos explorar como Nanostores permite o gerenciamento de estado compartilhado entre componentes de framework diferentes. Isso é particularmente útil quando você precisa sincronizar estado entre várias partes de sua aplicação.
Exemplo de Contador Compartilhado
Nós criaremos um contador compartilhado que pode ser atualizado e exibido entre componentes React, Vue, Svelte e Astro.
Primeiro, vamos criar nosso armazenamento de contador compartilhado:
// 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)
}
Agora, implementemos componentes em cada framework que usam este estado compartilhado:
Contador Compartilhado 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 Compartilhado 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 Compartilhado 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 Compartilhado 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>
Com este setup, todos esses componentes compartilharão o mesmo estado do contador. Ao incrementar ou decrementar o contador em qualquer componente, a alteração será refletida em todos os componentes, independentemente do framework utilizado.
Gestão de Estado Persistente
Enquanto estados independentes e compartilhados são poderosos, às vezes precisamos que o estado persista através de recarregamentos de página ou até mesmo por sessões do navegador. É aqui que o @nanostores/persistent
entra em cena. Vamos explorar como implementar um estado persistente em nosso projeto Astro.
Configurando o Estado Persistente
Primeiro, precisamos instalar o complemento persistente para Nanostores:
# Using npm
npm install @nanostores/persistent
# Using yarn
yarn add @nanostores/persistent
# Using pnpm
pnpm add @nanostores/persistent
Agora, vamos criar um contador persistente que manterá seu valor mesmo quando a página for recarregada:
// 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)
}
Neste exemplo, ‘persistentCount’ é a chave usada para armazenar o valor no localStorage, e 0 é o valor inicial.
Exemplo de Contador Persistente Multi-Framework
Vamos implementar um contador persistente usando componentes de frameworks diferentes. Este contador manterá seu valor através de recarregamentos de página e será acessível de qualquer framework.
Contador Persistente 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 Vue (Decréscimo)
<!-- 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 Svelte (Exibição)
<!-- src/components/SveltePersistentDisplay.svelte -->
<script>
import { persistentCount } from '../stores/persistentCounterStore'
</script>
<div>
Svelte Display: {$persistentCount}
</div>
Contador Persistente Astro (Redefinição)
---
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>
Agora, você pode usar esses componentes juntos em uma página 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 setup demonstra um contador persistente onde:
-
React gerencia o incremento
-
Vue gerencia o decremento
-
Svelte exibe o contador atual
-
Astro fornece um botão de reset
O valor do contador persistirá entre recarregamentos de página, mostrando o poder do @nanostores/persistent
na manutenção do estado.
Casos de Uso para Estado Persistente
O estado persistente é particularmente útil para:
-
Preferências do usuário (ex.: definições de tema, escolhas de idioma)
-
Formulários parcialmente preenchidos (para evitar perda de dados em recargas de página acidentais)
-
Tokens de autenticação (para manter as sessões do usuário)
-
Cache local de dados frequentemente acessados
Ao aproveitar o @nanostores/persistent
, você pode melhorar a experiência do usuário mantendo dados estado importante entre carregamentos de página e sessões do navegador.
Melhores Práticas e Dicas
Enquanto integra Nanostores em seus projetos Astro, tenha estas melhores práticas e dicas em mente para fazer o máximo desta solução de gerenciamento de estado leve.
1. Escolha o Tipo de Loja Certo
-
Use
atom
para estados simples, de valor único. -
Use
map
para estados de objeto com várias propriedades. -
Use
computed
para estados derivados que dependem de outros armazéns. -
Use
persistentAtom
oupersistentMap
quando você precisa que o estado persista após recarregamentos de página.
2. Mantenha os Armazéns pequenos e focados
Ao invés de criar armazéns grandes e monolíticos, prefira armazéns pequenos e focados. Esta abordagem melhora a manutenção e a performance permitindo atualizações mais 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 Armazéns Computados para Estado Derivado
Quando você tem estado que depende de outros pedaços de estado, use armazéns computados. Isto ajuda a manter seu estado DRY (Don’t Repeat Yourself) e garante que o estado derivado está sempre atualizado.
import { atom, computed } from 'nanostores'
const firstName = atom('John')
const lastName = atom('Doe')
const fullName = computed(
[firstName, lastName],
(first, last) => `${first} ${last}`
)
4. Aproveite o TypeScript para a Segurança de Tipo
Nanostores oferece excelente suporte a TypeScript. Use-o para capturar erros cedo e melhorar a experiência do desenvolvedor.
import { atom } from 'nanostores'
interface User {
id: number
name: string
}
const currentUser = atom<User | null>(null)
5. Considere o Desempenho em Aplicações grandes
Enquanto Nanostores é leve, tenha em mente o desempenho em aplicações maiores. Use a função batched
para agrupar várias atualizações de estoque juntas, reduzindo o número de re-renderizações.
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. Mantenha a Lógica Específica do Framework Separada
Ao usar Nanostores em um projeto Astro com múltiplos frameworks, tente manter a lógica de estado principal independente de framework. Isso facilita o compartilhamento de estado entre diferentes componentes de framework.
// 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. Use Armazenamentos Persistentes com Cautela
Embora os armazenamentos persistentes sejam poderosos, use-os com cuidado. Nem todo estado precisa persistir entre sessões. O uso excessivo de armazenamentos persistentes pode levar a comportamentos inesperados e possíveis problemas de desempenho.
8. Depuração de Nanostores
Para facilitar a depuração, você pode usar a função onMount
para registrar mudanças 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. Limpe as Inscrições
Ao usar Nanostores em componentes que podem ser desmontados, certifique-se de limpar as inscrições para evitar vazamentos de memória.
import { useEffect } from 'react'
import { count } from '../stores/countStore'
function Counter() {
useEffect(() => {
const unsubscribe = count.subscribe(() => {
// Do something
})
return unsubscribe
}, [])
// Rest of the component
}
Seguindo essas práticas recomendadas e dicas, você poderá gerenciar efetivamente o estado em seus projetos Astro usando Nanostores, independentemente dos frameworks que estiver integrando.
Conclusão
Como exploramos ao longo deste artigo, o Nanostores oferece uma solução poderosa e leve para gerenciamento de estado em projetos Astro, especialmente ao trabalhar com múltiplos frameworks. Vamos recapitular os principais pontos:
-
Versatilidade: O Nanostores integra-se perfeitamente com Astro, React, Vue, Svelte e Solid, tornando-o uma escolha ideal para projetos com múltiplos frameworks.
-
Simplicidade: Com sua API simples, as Nanostores oferecem uma curva de aprendizagem baixa enquanto ainda fornecem capacidades robustas de gerenciamento de estado.
-
Flexibilidade: De lojas atômicas simples a estados computados complexos e até mesmo armazenamento persistente, as Nanostores se adapta a uma ampla variedade de necessidades de gerenciamento de estado.
-
Performance: Sua natureza leve garante que as Nanostores não vão encher seu aplicativo, mantendo os benefícios de performance do Astro.
-
Melhores Práticas: Seguindo as diretrizes que mencionamos, como manter as lojas pequenas e focadas, aproveitando o TypeScript e usando lojas computadas para estados derivados, você pode criar sistemas de gerenciamento de estado eficientes e manutíveis.
Nanostores brilha na arquitetura de ilhas de componentes do Astro, permitindo que você gerencie estado em componentes interativos isolados de forma eficiente. Se você estiver construindo um simples site com alguns elementos interativos ou uma aplicação web complexa com várias frameworks, Nanostores fornece as ferramentas necessárias para gerenciar estado eficazmente.
Enquanto você prossegue sua jornada com Astro e Nanostores, lembre-se que a melhor forma de aprender é fazendo. Experimente diferentes tipos de store, tente implementar estado compartilhado entre frameworks e explorar as possibilidades de armazenamento persistente. Cada projeto trará novos desafios e oportunidades para refinar suas habilidades em gerenciamento de estado.
Fique de olho para os próximos artigos na nossa série “Nanostores no Astro: Uma Aventura com Múltiplos Frameworks”, onde nós mergulharmos mais fundo em aplicações práticas e técnicas avançadas para gerenciamento de estado em projetos Astro.
Recursos Adicionais
Para aprofundar seu entendimento de Nanostores e de seu uso em projetos Astro, verifique esses recursos valiosos:
- Repositório do Projeto de Demonstração no GitHub
- Site de Demonstração ao Vivo
- Documentação do Astro sobre Partilha de Estado
- Documentação Oficial de Nanostores
- Repositório Persistente do GitHub Nanostores
Feliz programação, e que seus projetos Astro sejam sempre estados e performáticos!
Source:
https://meirjc.hashnode.dev/state-management-in-astro-a-deep-dive-into-nanostores