Introdução
Seja bem-vindo ao primeiro episódio da série “Nanostores in Astro: A Multi-Framework Adventure”. Se você tiver dificuldades 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 se integra perfeitamente com a arquitetura de ilhas de componentes do Astro.
Neste artigo, nós vamos mergulhar em como Nanostores pode simplificar o gerenciamento de estado entre componentes Astro, React, Vue e Svelte. Vamos cobrir estado independente, estado compartilhado e apresentar gerenciamento de estado persistente. Vamos embarcar nesta viagem para simplificar o gerenciamento de estado em nossos projetos Astro.
Resumo rápido
Se você está ansioso para mergulhar diretamente, 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 cobrimos gerenciamento de estado independente, compartilhado e persistente através de Astro, React, Vue e Svelte.
-
Veja esses recursos:
Feel free to explore the demo and code alongside this article for a hands-on learning experience!
Understanding Nanostores
What are 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 da sua aplicação.
Por que usar Nanostores em Astro?
-
Leve e Rápido: Nanostores é incrivelmente pequeno (Entre 265 e 814 bytes), garantindo que não aumentará o tamanho do pacote de sua aplicação.
-
Agnóstico de Framework: Perfeito para o ecossistema multi-framework de Astro. Integra-se com facilidade com React, Vue, Svelte, Solid e JavaScript vanilla.
-
API Simples: Sem configuração complexa ou boilerplate. É simples e intuitivo de usar.
-
Complementa as Ilhas de Componentes do Astro: Nanostores melhora a arquitetura de ilhas de componentes do Astro, permitindo o gerenciamento eficiente de estado em componentes interativos isolados.
Conceitos Básicos
Nanostores se baseia em três conceitos principais:
-
Átomos: Armazenamentos simples que mantê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 ver 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 no 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 frameworks diferentes. O Nanostores torna isso sinuoso. Vamos explorar como implementar o gerenciamento de estado independente através de 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, vamos implementar 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 estado independente 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 o Nanostores permite o estado compartilhado entre componentes de frameworks diferentes. Isso é particularmente útil quando você precisa sincronizar estados entre várias partes de sua aplicação.
Exemplo de Contador Compartilhado
Nós criaremos um contador compartilhado que possa ser atualizado e exibido através de componentes React, Vue, Svelte e Astro.
Primeiro, vamos criar nosso 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, vamos implementar 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 esta configuração, todos esses componentes irão compartilhar o mesmo estado do contador. Incrementar ou decrementar o contador em qualquer componente atualizará o valor em todos os componentes, independentemente do framework usado.
Gerenciamento de Estado Persistente
Enquanto estados independentes e compartilhados são poderosos, às vezes precisamos que o nosso estado persista através de recargas 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 Multiframework
Vamos implementar um contador persistente usando componentes de frameworks diferentes. Este contador manterá seu valor através de recargas 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émento)
<!-- 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 (Redefinir)
---
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>
Esta configuração demonstra um contador persistente onde:
-
React manipula o incremento
-
Vue manipula 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 de usuário (ex.: configurações de tema, escolhas de idioma)
-
Formulários parcialmente preenchidos (para evitar perda de dados em recarregamentos de página acidentais)
-
Tokens de autenticação (para manter as sessões de usuário)
-
Cache local de dados frequentemente acessados
Ao aproveitar o @nanostores/persistent
, você pode melhorar a experiência do usuário mantendo dados de estado importante entre carregamentos de página e sessões do navegador.
Melhores Práticas e Dicas
Enquanto você integra Nanostores em seus projetos Astro, tenha em mente estas melhores práticas e dicas para aproveitar ao máximo esta 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 através de recarregamentos de página.
2. Mantenha os Armazéns Pequenos e Focusados
Em vez 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ê tiver estado que depende de outros pedaços de estado, use armazéns computados. Isto ajuda a manter seu estado DRY (Não Repita Si Mesmo) 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 a TypeScript para a Segurança de Tipo
Nanostores oferece excelente suporte a TypeScript. Use-o para capturar erros ao longo 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 armazém 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 Lógica Específica do Framework Separada
Ao usar Nanostores em um projeto Astro com múltiplos frameworks, tente manter a lógica principal de estado independente do 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 Critério
Embora os armazenamentos persistentes sejam poderosos, use-os de forma criteriosa. Nem todo estado precisa persistir entre sessões. O uso excessivo de armazenamentos persistentes pode levar a comportamentos inesperados e a 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 Assinaturas
Ao usar Nanostores em componentes que podem ser desmontados, certifique-se de limpar as assinaturas 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 melhores práticas e dicas, você será capaz de gerenciar efetivamente o estado em seus projetos Astro usando Nanostores, independentemente de quais frameworks você está 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: Nanostores se integra 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 oferecem capacidades robustas de gerenciamento de estado.
- Flexibilidade: De lojas atômicas simples a estados computados complexos e até mesmo armazenamento persistente, as Nanostores se adapam 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 discutimos, 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 manutenveis.
Nanostores brilha na arquitetura de ilhas de componentes do Astro, permitindo que você gerencie estado através de componentes interativos isolados eficientemente. Se você estiver construindo um site simples com alguns elementos interativos ou uma aplicação web complexa com vários frameworks, Nanostores fornece as ferramentas necessárias para gerenciar estado efetivamente.
Enquanto você continua sua jornada com Astro e Nanostores, lembre-se que a melhor forma de aprender é fazendo. Experimente diferentes tipos de loja, tente implementar estado compartilhado entre frameworks e explore as possibilidades de armazenamento persistente. Cada projeto trará novos desafios e oportunidades para refinar suas habilidades de gerenciamento de estado.
Fique de olho para os próximos artigos na nossa série “Nanostores no Astro: Uma Aventura com Multiframework”, 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 sua utilização em projetos Astro, confira estas fontes valiosas:
- 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 para Nanostores
Feliz codificação, e que seus projetos Astro sejam sempre estado eficientes!
Source:
https://meirjc.hashnode.dev/state-management-in-astro-a-deep-dive-into-nanostores