Funções auxiliares para gerenciar estado persistente no VanJS, mantendo valores automaticamente sincronizados com localStorage/sessionStorage.
persistentItem(key, defaultValue, options) Cria um estado reativo do VanJS que persiste automaticamente no storage.
Parâmetros:
key (string): Chave única para armazenar o valordefaultValue (any): Valor padrão caso não exista no storageoptions (object): Configurações opcionaisOpções disponíveis:
{
storage: localStorage, // ou sessionStorage
serialize: JSON.stringify, // função de serialização
deserialize: JSON.parse, // função de deserialização
validate: null, // função de validação (value) => boolean
onError: console.error // handler de erros
}
Retorna: Um estado VanJS com método .clear() adicional
persistentGroup(prefix, defaults, options) Cria um grupo de estados persistentes relacionados com um prefixo comum.
Parâmetros:
prefix (string): Prefixo para as chaves (será adicionado como prefix_key)defaults (object): Objeto com pares chave/valor padrãooptions (object): Mesmas opções do persistentItemRetorna: Objeto com estados e método .clearAll()
Quando usar cada função:
persistentItem para valores únicos (contador, flag, array de itens, etc.)persistentGroup quando precisar acessar múltiplos valores separadamente (formulário, configurações, etc.)// Criar um contador persistente
const counter = persistentItem('myCounter', 0)
// Usar no componente
const CounterApp = () => div(
button({ onclick: () => counter.val++ }, '➕'),
span(` Contador: ${counter.val} `),
button({ onclick: () => counter.val-- }, '➖'),
button({ onclick: () => counter.clear() }, '🗑️ Limpar')
)
// Contadores independentes de Like e Deslike
const counters = persistentGroup('counters', {
likes: 0,
deslikes: 0
})
const LikeDislikeApp = () => span(
'👍 ', counters.likes, ' ',
button({onclick: () => ++counters.likes.val}, '➕'),
' | ',
'👎 ', counters.deslikes, ' ',
button({onclick: () => ++counters.deslikes.val}, '➕'),
' ',
button({onclick: () => counters.clearAll()}, '🗑️')
)
// Idade que só aceita valores entre 0 e 120
const age = persistentItem('userAge', 18, {
validate: (value) => typeof value === 'number' && value >= 0 && value <= 120,
onError: (msg, error) => {
console.warn('Valor inválido detectado:', msg)
// Aqui você pode mostrar uma notificação ao usuário
}
})
// Configurações de tema (use persistentGroup para acessar propriedades separadamente)
const theme = persistentGroup('theme', {
mode: 'light',
primaryColor: '#3b82f6',
fontSize: 16
})
// Atualizar propriedades individuais é mais fácil
button({
onclick: () => {
theme.mode.val = theme.mode.val === 'light' ? 'dark' : 'light'
}
}, 'Alternar Tema')
// Contador temporário que será limpo ao fechar o navegador
const tempCounter = persistentItem('tempCounter', 0, {
storage: sessionStorage
})
// Ou para múltiplos valores temporários
const sessionData = persistentGroup('session', {
visitCount: 0,
lastAction: ''
}, {
storage: sessionStorage
})
// Formulário de usuário com múltiplos campos
const userForm = persistentGroup('userForm', {
name: '',
email: '',
age: 0,
newsletter: false
})
// Usar no componente
const FormApp = () => div(
input({
value: userForm.name.val,
oninput: e => userForm.name.val = e.target.value,
placeholder: 'Nome'
}),
input({
value: userForm.email.val,
oninput: e => userForm.email.val = e.target.value,
placeholder: 'Email'
}),
input({
type: 'number',
value: userForm.age.val,
oninput: e => userForm.age.val = parseInt(e.target.value) || 0,
placeholder: 'Idade'
}),
label(
input({
type: 'checkbox',
checked: userForm.newsletter.val,
onchange: e => userForm.newsletter.val = e.target.checked
}),
' Receber newsletter'
),
button({ onclick: () => userForm.clearAll() }, 'Limpar Tudo')
)
const todos = persistentItem('todoList', [])
const TodoApp = () => {
const newTodo = van.state('')
const addTodo = () => {
if (newTodo.val.trim()) {
todos.val = [...todos.val, {
id: Date.now(),
text: newTodo.val,
done: false
}]
newTodo.val = ''
}
}
const toggleTodo = (id) => {
todos.val = todos.val.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
)
}
const removeTodo = (id) => {
todos.val = todos.val.filter(todo => todo.id !== id)
}
return div(
input({
value: newTodo.val,
oninput: e => newTodo.val = e.target.value,
placeholder: 'Nova tarefa...'
}),
button({ onclick: addTodo }, 'Adicionar'),
ul(
todos.val.map(todo =>
li(
input({
type: 'checkbox',
checked: todo.done,
onchange: () => toggleTodo(todo.id)
}),
span({
style: () => todo.done ? 'text-decoration: line-through' : ''
}, todo.text),
button({ onclick: () => removeTodo(todo.id) }, '❌')
)
)
),
todos.val.length > 0 && button({ onclick: () => todos.clear() }, 'Limpar Todas')
)
}
const cart = persistentGroup('shoppingCart', {
items: [],
total: 0,
coupon: ''
})
const addToCart = (product) => {
cart.items.val = [...cart.items.val, product]
cart.total.val = cart.items.val.reduce((sum, item) => sum + item.price, 0)
}
const CartApp = () => div(
h2('Carrinho de Compras'),
div(`Itens: ${cart.items.val.length}`),
div(`Total: R$ ${cart.total.val.toFixed(2)}`),
button({ onclick: () => cart.clearAll() }, 'Esvaziar Carrinho')
)
const uiPrefs = persistentGroup('ui', {
sidebarOpen: true,
darkMode: false,
fontSize: 16,
language: 'pt-BR'
})
// Aplicar preferências automaticamente
van.derive(() => {
document.documentElement.style.fontSize = `${uiPrefs.fontSize.val}px`
document.documentElement.classList.toggle('dark', uiPrefs.darkMode.val)
document.documentElement.lang = uiPrefs.language.val
})
const SettingsPanel = () => div(
label(
input({
type: 'checkbox',
checked: uiPrefs.darkMode.val,
onchange: e => uiPrefs.darkMode.val = e.target.checked
}),
' Modo Escuro'
),
label(
'Tamanho da Fonte: ',
input({
type: 'range',
min: 12,
max: 24,
value: uiPrefs.fontSize.val,
oninput: e => uiPrefs.fontSize.val = parseInt(e.target.value)
}),
` ${uiPrefs.fontSize.val}px`
)
)
.clear() Remove o item do storage e restaura o valor padrão:
const counter = persistentItem('count', 0)
counter.val = 10
counter.clear() // Remove do localStorage e volta para 0
.clearAll() Remove todos os itens de um grupo:
const formData = persistentGroup('form', { name: '', email: '' })
formData.clearAll() // Limpa todos os campos
As funções lidam automaticamente com erros comuns:
onError será chamadoconst counter = persistentItem('myCounter', 0, {
onError: (message, error) => {
console.error('Erro no estado persistente:', message, error)
// Mostrar notificação ao usuário
showNotification('Erro ao salvar dados', 'error')
}
})