tutoriales.com

Gestión de Estado Reactiva con Svelte Stores: Una Guía Completa

Este tutorial te guiará a través del concepto de Svelte Stores, una herramienta poderosa para la gestión de estado reactiva en tus aplicaciones Svelte. Aprenderás a crear, usar y derivar diferentes tipos de stores para construir aplicaciones más robustas y fáciles de mantener. Cubriremos stores grabables, legibles y derivadas con ejemplos prácticos.

Intermedio20 min de lectura5 views16 de marzo de 2026Reportar error

La gestión de estado es uno de los pilares fundamentales en el desarrollo de aplicaciones web modernas. En Svelte, a diferencia de otros frameworks que a menudo requieren librerías externas complejas para este propósito, la gestión de estado se simplifica enormemente gracias a los Svelte Stores. Estas no son más que objetos con una interfaz mínima que permiten suscribirse a los cambios y emitir notificaciones cuando su valor se actualiza, facilitando la reactividad a lo largo de toda tu aplicación.

En este tutorial, profundizaremos en los Svelte Stores, explorando sus diferentes tipos y cómo puedes utilizarlos para crear aplicaciones Svelte más eficientes, mantenibles y, sobre todo, reactivas. Prepárate para dominar esta característica esencial de Svelte.

🚀 ¿Qué son los Svelte Stores?

En su esencia, un Svelte Store es un contenedor para un valor que puede ser accedido y modificado por múltiples componentes, y que notifica a todos los componentes suscritos cuando su valor cambia. Esto permite una gestión de estado centralizada y reactiva, eliminando la necesidad de pasar propiedades manualmente a través de múltiples niveles de componentes (conocido como "prop drilling").

Los Stores siguen un contrato simple, definido por una interfaz con dos métodos principales:

  • .subscribe(callback): Registra una función callback que se ejecutará cada vez que el valor del store cambie. Retorna una función para cancelar la suscripción.
  • .set(value): (Solo para stores grabables) Actualiza el valor del store, notificando a todos los suscriptores.

Svelte nos proporciona helpers para crear diferentes tipos de stores, que veremos en detalle a continuación.

💡 Consejo: Piensa en los Stores como una fuente de datos única a la que múltiples componentes pueden 'escuchar'. Cuando esa fuente cambia, todos los oyentes reaccionan automáticamente.

🛠️ Tipos de Svelte Stores

Svelte ofrece tres tipos principales de stores, cada uno diseñado para escenarios específicos:

  1. Writable Stores: Los más comunes, permiten leer y escribir valores.
  2. Readable Stores: Solo permiten leer valores, útiles para datos que no deben ser modificados directamente por los componentes.
  3. Derived Stores: Permiten crear un store cuyo valor depende de uno o más stores existentes.

Analicemos cada uno con ejemplos prácticos.


1. ✍️ Writable Stores: La Base de la Reactividad

Los writable stores son la piedra angular de la gestión de estado en Svelte. Permiten que cualquier componente los actualice y que otros componentes se suscriban a sus cambios. Son ideales para el estado de la aplicación que necesita ser modificado, como el usuario autenticado, la lista de tareas, el tema actual de la interfaz, etc.

Creando un Writable Store

Para crear un writable store, importamos la función writable desde svelte/store y la invocamos con un valor inicial.

// src/stores.js
import { writable } from 'svelte/store';

export const count = writable(0);
export const user = writable({ name: 'Invitado', isLoggedIn: false });
export const theme = writable('light');

Suscribiéndose a un Writable Store en Componentes

Existen dos maneras principales de suscribirse a un store en un componente Svelte:

a) Suscripción Explícita (Menos Común para Reactividad Directa)

Esta es la forma más 'manual' y útil cuando necesitas controlar el ciclo de vida de la suscripción, por ejemplo, para realizar efectos secundarios o limpiar recursos. Requiere que te suscribas y te desuscribas explícitamente.

<!-- src/lib/ExplicitCounter.svelte -->
<script>
  import { onMount, onDestroy } from 'svelte';
  import { count } from '../stores.js';

  let currentCount;
  let unsubscribe;

  onMount(() => {
    unsubscribe = count.subscribe(value => {
      currentCount = value;
    });
  });

  onDestroy(() => {
    unsubscribe(); // ¡Importante desuscribirse para evitar fugas de memoria!
  });

  function increment() {
    count.update(n => n + 1);
  }
</script>

<p>Conteo explícito: {currentCount}</p>
<button on:click={increment}>Incrementar</button>
b) Suscripción Automática con el Prefijo $ (La Forma Svelte)

Esta es la forma idiomática y recomendada de usar stores en Svelte. Svelte detecta el prefijo $ antes del nombre del store y automáticamente se suscribe y desuscribe por ti, manejando la reactividad de forma mágica. ¡Es mucho más conciso y potente!

<!-- src/lib/AutoCounter.svelte -->
<script>
  import { count } from '../stores.js';

  function increment() {
    $count++; // Svelte desazucarará esto a count.set($count + 1);
  }
</script>

<p>Conteo automático: {$count}</p>
<button on:click={increment}>Incrementar</button>
<button on:click={() => $count = 0}>Resetear</button>

Actualizando un Writable Store

Los writable stores tienen tres métodos para modificar su valor:

  • .set(newValue): Establece un nuevo valor para el store, sobrescribiendo el anterior.
  • .update(callback): Toma una función callback que recibe el valor actual del store y debe devolver el nuevo valor. Esto es útil para actualizaciones basadas en el estado actual.
  • Asignación con $ (solo en archivos .svelte): Como vimos, $count++ es un azúcar sintáctico para $count.update(n => n + 1). $count = newValue es azúcar sintáctico para $count.set(newValue).
// Ejemplo de uso en JS (no en un componente Svelte)
import { count } from './stores.js';

count.set(10); // Establece el conteo a 10
console.log(count); // Esto imprimirá el objeto store, no el valor directamente

// Para obtener el valor fuera de un componente Svelte
let currentValue;
const unsubscribe = count.subscribe(value => {
  currentValue = value;
});
console.log(currentValue); // 10
unsubscribe();

count.update(n => n + 5); // Incrementa el conteo en 5 (ahora 15)

// Podemos hacer operaciones complejas
import { user } from './stores.js';
user.update(u => ({
  ...u,
  isLoggedIn: true,
  name: 'Juanito'
}));
📌 Nota: Cuando usas la sintaxis `$` en un componente Svelte para asignar un valor (`$store = valor`), Svelte lo convierte internamente a `store.set(valor)`. Si lo usas para modificarlo (`$store++`), Svelte lo convierte a `store.update(current => current + 1)`.

Ejemplo Completo de Writable Store: Lista de Tareas (Todo List)

Vamos a construir una pequeña aplicación de lista de tareas para ver writable stores en acción.

// src/stores/todoStore.js
import { writable } from 'svelte/store';

const initialTodos = [
  { id: 1, text: 'Aprender Svelte Stores', completed: false },
  { id: 2, text: 'Construir una app con Svelte', completed: false },
  { id: 3, text: 'Dominar la gestión de estado', completed: true },
];

export const todos = writable(initialTodos);

export const addTodo = (text) => {
  todos.update(currentTodos => [
    ...currentTodos,
    { id: Date.now(), text, completed: false },
  ]);
};

export const toggleTodo = (id) => {
  todos.update(currentTodos =>
    currentTodos.map(todo =>
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    )
  );
};

export const removeTodo = (id) => {
  todos.update(currentTodos => currentTodos.filter(todo => todo.id !== id));
};

export const clearCompleted = () => {
  todos.update(currentTodos => currentTodos.filter(todo => !todo.completed));
};
<!-- src/routes/TodoApp.svelte -->
<script>
  import { todos, addTodo, toggleTodo, removeTodo, clearCompleted } from '../stores/todoStore.js';

  let newTodoText = '';

  function handleAddTodo() {
    if (newTodoText.trim()) {
      addTodo(newTodoText);
      newTodoText = '';
    }
  }
</script>

<div class="todo-container">
  <h1>Mi Lista de Tareas Svelte</h1>

  <input
    type="text"
    placeholder="Añadir nueva tarea..."
    bind:value={newTodoText}
    on:keydown={(e) => { if (e.key === 'Enter') handleAddTodo(); }}
  />
  <button on:click={handleAddTodo}>Añadir Tarea</button>

  {#if $todos.length === 0}
    <p>¡No hay tareas pendientes! 🎉</p>
  {:else}
    <ul>
      {#each $todos as todo (todo.id)}
        <li>
          <input
            type="checkbox"
            checked={todo.completed}
            on:change={() => toggleTodo(todo.id)}
          />
          <span class={todo.completed ? 'completed' : ''}>
            {todo.text}
          </span>
          <button on:click={() => removeTodo(todo.id)}>🗑️</button>
        </li>
      {/each}
    </ul>
  {/if}

  <button on:click={clearCompleted} disabled={!$todos.some(t => t.completed)}>
    Limpiar Tareas Completadas
  </button>
</div>

<style>
  .todo-container {
    max-width: 500px;
    margin: 2em auto;
    padding: 1.5em;
    border: 1px solid #eee;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.05);
  }
  ul {
    list-style: none;
    padding: 0;
  }
  li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.8em 0;
    border-bottom: 1px dotted #eee;
  }
  li:last-child {
    border-bottom: none;
  }
  li span {
    flex-grow: 1;
    margin-left: 0.5em;
  }
  li span.completed {
    text-decoration: line-through;
    color: #999;
  }
  input[type="text"] {
    width: calc(100% - 100px); /* Ajustar el ancho */
    padding: 0.8em;
    margin-right: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
  }
  button {
    padding: 0.8em 1.2em;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
  button:hover:not(:disabled) {
    background-color: #0056b3;
  }
  button:disabled {
    background-color: #cccccc;
    cursor: not-allowed;
  }
  button:last-of-type { /* Botón de limpiar */
    background-color: #dc3545;
    margin-top: 1em;
  }
  button:last-of-type:hover:not(:disabled) {
    background-color: #c82333;
  }
</style>
Writable Stores Dominados

2. 👁️ Readable Stores: Solo para Lectura

Los readable stores son similares a los writable stores, pero no exponen los métodos set o update. Esto los hace perfectos para exponer datos que no deben ser modificados directamente por los componentes de la interfaz de usuario, sino por una lógica centralizada (por ejemplo, servicios de API, datos de configuración inicial, etc.).

Creando un Readable Store

Se crean con la función readable de svelte/store. Acepta un valor inicial y, opcionalmente, una función start que se ejecuta cuando el primer suscriptor se une al store, y una función stop que se ejecuta cuando el último suscriptor se va.

// src/stores/timeStore.js
import { readable } from 'svelte/store';

export const time = readable(new Date(), function start(set) {
  const interval = setInterval(() => {
    set(new Date());
  }, 1000);

  // La función stop se llama cuando no hay más suscriptores
  return function stop() {
    clearInterval(interval);
  };
});

// Otro ejemplo: un store de configuración
export const appConfig = readable({
  appName: 'Svelte App',
  version: '1.0.0',
  apiUrl: 'https://api.example.com',
});

Usando un Readable Store

Su uso en componentes es idéntico al de los writable stores, empleando el prefijo $. La diferencia clave es que no puedes intentar modificar su valor directamente ($time = new Date() daría un error).

<!-- src/lib/Clock.svelte -->
<script>
  import { time } from '../stores/timeStore.js';

  // El valor de $time se actualizará cada segundo automáticamente
  // y el componente se re-renderizará.
</script>

<div class="clock">
  <p>Hora actual: {$time.toLocaleTimeString()}</p>
  <p>Fecha actual: {$time.toLocaleDateString()}</p>
</div>

<style>
  .clock {
    border: 1px solid #ccc;
    padding: 1em;
    border-radius: 4px;
    background-color: #f9f9f9;
    text-align: center;
    margin-top: 1em;
  }
  .clock p {
    margin: 0.5em 0;
    font-size: 1.2em;
  }
</style>
🔥 Importante: Las funciones `start` y `stop` de un `readable` store son cruciales para gestionar recursos externos. Asegúrate de liberar cualquier recurso (intervalos, escuchadores de eventos, conexiones WebSocket, etc.) en la función `stop` para evitar fugas de memoria y optimizar el rendimiento.

3. 🎣 Derived Stores: La Magia de los Datos Derivados

Los derived stores son increíblemente poderosos. Permiten crear un nuevo store cuyo valor se calcula a partir de uno o más stores existentes. Siempre son readable (no puedes modificarlos directamente) y se actualizan automáticamente cuando cualquiera de sus stores dependientes cambia.

Son perfectos para transformar datos, filtrar listas, o combinar información de múltiples fuentes reactivas.

Creando un Derived Store

Se crean con la función derived de svelte/store. Puede tomar uno o varios stores como primer argumento y una función callback como segundo. La callback recibe los valores de los stores dependientes y debe retornar el valor derivado.

// src/stores/todoStore.js (continuación)
import { writable, derived } from 'svelte/store';
// ... (todos los exports anteriores de todos, addTodo, etc.)

// Store derivado para tareas completadas
export const completedTodos = derived(todos, ($todos) =>
  $todos.filter(todo => todo.completed)
);

// Store derivado para tareas pendientes
export const pendingTodos = derived(todos, ($todos) =>
  $todos.filter(todo => !todo.completed)
);

// Store derivado para el progreso total
export const todoProgress = derived(todos, ($todos) => {
  const total = $todos.length;
  if (total === 0) return 0;
  const completedCount = $todos.filter(todo => todo.completed).length;
  return Math.round((completedCount / total) * 100);
});

Usando un Derived Store

Al igual que con los otros stores, simplemente usa el prefijo $ en tus componentes.

<!-- src/routes/TodoApp.svelte (continuación) -->
<script>
  import { todos, addTodo, toggleTodo, removeTodo, clearCompleted,
           completedTodos, pendingTodos, todoProgress } from '../stores/todoStore.js';

  let newTodoText = '';

  function handleAddTodo() {
    if (newTodoText.trim()) {
      addTodo(newTodoText);
      newTodoText = '';
    }
  }
</script>

<div class="todo-container">
  <h1>Mi Lista de Tareas Svelte</h1>

  <input
    type="text"
    placeholder="Añadir nueva tarea..."
    bind:value={newTodoText}
    on:keydown={(e) => { if (e.key === 'Enter') handleAddTodo(); }}
  />
  <button on:click={handleAddTodo}>Añadir Tarea</button>

  <div class="summary">
    <p>Tareas pendientes: {$pendingTodos.length}</p>
    <p>Tareas completadas: {$completedTodos.length}</p>
    <p>Progreso total: <span class="badge blue">{$todoProgress}%</span></p>
    <div class="progress-bar">
      <div class="progress-fill" style="width: {$todoProgress}%; background: #28a745;">{$todoProgress}%</div>
    </div>
  </div>

  {#if $todos.length === 0}
    <p>¡No hay tareas pendientes! 🎉</p>
  {:else}
    <ul>
      {#each $todos as todo (todo.id)}
        <li>
          <input
            type="checkbox"
            checked={todo.completed}
            on:change={() => toggleTodo(todo.id)}
          />
          <span class={todo.completed ? 'completed' : ''}>
            {todo.text}
          </span>
          <button on:click={() => removeTodo(todo.id)}>🗑️</button>
        </li>
      {/each}
    </ul>
  {/if}

  <button on:click={clearCompleted} disabled={!$todos.some(t => t.completed)}>
    Limpiar Tareas Completadas
  </button>
</div>

<style>
  /* Estilos anteriores */
  .todo-container {
    max-width: 500px;
    margin: 2em auto;
    padding: 1.5em;
    border: 1px solid #eee;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.05);
  }
  ul {
    list-style: none;
    padding: 0;
  }
  li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.8em 0;
    border-bottom: 1px dotted #eee;
  }
  li:last-child {
    border-bottom: none;
  }
  li span {
    flex-grow: 1;
    margin-left: 0.5em;
  }
  li span.completed {
    text-decoration: line-through;
    color: #999;
  }
  input[type="text"] {
    width: calc(100% - 100px); /* Ajustar el ancho */
    padding: 0.8em;
    margin-right: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
  }
  button {
    padding: 0.8em 1.2em;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
  button:hover:not(:disabled) {
    background-color: #0056b3;
  }
  button:disabled {
    background-color: #cccccc;
    cursor: not-allowed;
  }
  button:last-of-type { /* Botón de limpiar */
    background-color: #dc3545;
    margin-top: 1em;
  }
  button:last-of-type:hover:not(:disabled) {
    background-color: #c82333;
  }
  .summary {
    background-color: #eaf6ff;
    border-left: 5px solid #007bff;
    padding: 1em;
    margin: 1.5em 0;
    border-radius: 4px;
  }
  .summary p {
    margin: 0.5em 0;
  }
</style>
💡 Consejo: Los `derived` stores son perfectos para separar la lógica de presentación y cálculo de los datos brutos. Mantén tus `writable` stores lo más simples posible y usa `derived` para cualquier computación compleja.

Derived Store con set asíncrono

La función derived también puede aceptar un tercer argumento: una función set para manejar valores asíncronos o con efectos secundarios. Esto es útil para stores que dependen de promesas o de lógica que tarda tiempo en resolverse.

// src/stores/asyncUserStore.js
import { readable, derived } from 'svelte/store';

// Simula una llamada a API
async function fetchUser(userId) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        id: userId,
        name: 'Alice',
        email: 'alice@example.com',
        preferences: { theme: 'dark' }
      });
    }, 1500);
  });
}

export const currentUserId = writable(1);

// Derived store que simula la carga de un usuario por su ID
export const currentUser = derived(
  currentUserId,
  ($userId, set) => {
    set(null); // Establece el usuario a null mientras se carga
    fetchUser($userId).then(user => {
      set(user);
    });
    return () => {}; // Función de limpieza opcional
  },
  null // Valor inicial opcional antes de la primera actualización
);
<!-- src/lib/UserDetails.svelte -->
<script>
  import { currentUserId, currentUser } from '../stores/asyncUserStore.js';

  function changeUser() {
    currentUserId.set($currentUserId === 1 ? 2 : 1); // Cambia el ID para cargar otro usuario
  }
</script>

<div class="user-details">
  <h2>Detalles del Usuario</h2>
  {#if $currentUser}
    <p>ID: {$currentUser.id}</p>
    <p>Nombre: {$currentUser.name}</p>
    <p>Email: {$currentUser.email}</p>
    <p>Tema Preferido: {$currentUser.preferences.theme}</p>
  {:else}
    <p>Cargando usuario...</p>
  {/if}
  <button on:click={changeUser}>Cargar Otro Usuario</button>
</div>

<style>
  .user-details {
    border: 1px solid #ddd;
    padding: 1.5em;
    margin: 2em auto;
    max-width: 400px;
    border-radius: 8px;
    background-color: #f0f8ff;
  }
  .user-details h2 {
    color: #2c3e50;
  }
  .user-details p {
    margin: 0.5em 0;
  }
  .user-details button {
    margin-top: 1em;
    background-color: #28a745;
  }
  .user-details button:hover {
    background-color: #218838;
  }
</style>

Este ejemplo ilustra cómo un derived store puede manejar valores asíncronos, proporcionando un valor intermedio (null en este caso) mientras la operación asíncrona está en curso. La función set se usa para actualizar el valor del store cuando la promesa se resuelve.

Derived Stores Avanzados

🗺️ Organización de Stores en tu Proyecto

Es una buena práctica organizar tus stores en un directorio src/stores (o similar) para mantener tu código limpio y modular. Cada store o grupo de stores relacionados puede tener su propio archivo.

my-svelte-app/
├── src/
│   ├── App.svelte
│   ├── main.js
│   └── stores/
│       ├── index.js             // Exporta todos los stores para una importación centralizada
│       ├── countStore.js        // Para un simple contador
│       ├── userStore.js         // Para el estado del usuario
│       ├── todoStore.js         // Para la lógica de la lista de tareas
│       └── themeStore.js        // Para el tema de la aplicación
└── package.json

src/stores/index.js (Ejemplo de exportación centralizada):

// src/stores/index.js
export * from './countStore.js';
export * from './userStore.js';
export * from './todoStore.js';
export * from './themeStore.js';
// etc.

Esto permite importar múltiples stores desde un solo lugar:

import { count, user, todos } from '../stores';
Svelte Store Estado Global Componente A Componente B Componente C set / update subscribe ($) set / update subscribe ($) subscribe ($)

🔄 Store Contratos Personalizados

Si bien writable, readable y derived cubren la mayoría de los casos de uso, puedes crear tus propios stores personalizados que cumplan con la interfaz del store. Esto te da flexibilidad total para implementar lógica compleja, como persistencia en localStorage o integración con WebSockets.

Vamos a crear un store personalizado que guarde y cargue su valor desde localStorage.

// src/stores/persistentStore.js
import { writable } from 'svelte/store';

export function persistentWritable(key, initialValue) {
  // Intenta cargar el valor de localStorage
  const storedValue = localStorage.getItem(key);
  const data = storedValue ? JSON.parse(storedValue) : initialValue;

  const store = writable(data);

  store.subscribe(value => {
    // Cuando el store cambia, guarda el nuevo valor en localStorage
    localStorage.setItem(key, JSON.stringify(value));
  });

  return store;
}
// src/stores/index.js (actualizado)
// ...
export * from './persistentStore.js';
<!-- src/routes/Settings.svelte -->
<script>
  import { persistentWritable } from '../stores/persistentStore.js';

  export const appTheme = persistentWritable('appTheme', 'light');
  export const showWelcomeMessage = persistentWritable('showWelcome', true);
</script>

<div class="settings">
  <h2>Configuración de la Aplicación</h2>

  <label>
    Tema de la Interfaz:
    <select bind:value={$appTheme}>
      <option value="light">Claro</option>
      <option value="dark">Oscuro</option>
    </select>
  </label>

  <label>
    <input type="checkbox" bind:checked={$showWelcomeMessage} />
    Mostrar mensaje de bienvenida
  </label>

  <p>El tema actual es: {$appTheme}</p>
  <p>El mensaje de bienvenida está: {$showWelcomeMessage ? 'activado' : 'desactivado'}</p>
</div>

<style>
  .settings {
    max-width: 400px;
    margin: 2em auto;
    padding: 1.5em;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    background-color: #ffffff;
    box-shadow: 0 4px 12px rgba(0,0,0,0.08);
  }
  .settings h2 {
    color: #333;
    margin-bottom: 1.5em;
    text-align: center;
  }
  .settings label {
    display: flex;
    align-items: center;
    margin-bottom: 1em;
    font-size: 1.1em;
    color: #555;
  }
  .settings select, .settings input[type="checkbox"] {
    margin-left: 10px;
    padding: 0.5em;
    border-radius: 4px;
    border: 1px solid #ccc;
    font-size: 1em;
  }
  .settings select {
    flex-grow: 1;
  }
  .settings input[type="checkbox"] {
    width: 20px;
    height: 20px;
  }
  .settings p {
    margin-top: 1.5em;
    font-style: italic;
    color: #777;
    border-top: 1px dashed #eee;
    padding-top: 1em;
  }
</style>

Este persistentWritable es un writable store normal, pero con la lógica adicional de interactuar con localStorage cada vez que su valor cambia (a través de subscribe). Cuando se inicializa, intenta cargar el valor existente, haciendo que el estado persista entre recargas de página.

🧐 ¿Por qué Svelte Stores son tan eficientes? Svelte compila tu código a JavaScript Vanilla, y el sistema de reactividad de los stores es muy ligero. Cuando un store se actualiza, Svelte solo re-renderiza los componentes o partes de los componentes que realmente utilizan ese store. No hay Virtual DOM ni comparaciones costosas, lo que resulta en un rendimiento excelente.

🎯 Buenas Prácticas y Patrones con Svelte Stores

  • Centralización: Mantén tus stores en un directorio específico (src/stores).
  • Modularidad: Separa la lógica de cada store en archivos individuales. Por ejemplo, userStore.js, settingsStore.js, todoStore.js.
  • Estado Mínimo: No guardes todo en stores. Para el estado local de un componente que no necesita ser compartido, usa let y $ normales dentro del <script> del componente.
  • Funciones de Ayuda: Exporta funciones que interactúen con tus stores (como addTodo, toggleTodo, etc.) junto con el store mismo. Esto encapsula la lógica de negocio y evita que los componentes accedan directamente a store.update() o store.set(), haciéndolos más fáciles de probar y mantener.
  • Lectura vs. Escritura: Utiliza readable stores para datos que no deben ser modificados por la UI, y writable para datos que sí lo son. Los derived stores son siempre readable.
  • Asincronía: Cuando manejes operaciones asíncronas (como llamadas a API), considera cómo el store representará los estados de carga, error y éxito. Puedes tener un store para los datos, otro para el estado de carga (isLoading: writable(false)), y otro para errores (error: writable(null)).

⚠️ Consideraciones y Errores Comunes

  • Olvidar $: El error más común. Si intentas usar un store sin el prefijo $, Svelte no lo tratará como un valor reactivo y no se suscribirá automáticamente. Accederás al objeto store en sí, no a su valor.
  • Fugas de Memoria: Si usas store.subscribe() directamente en onMount y no limpias la suscripción en onDestroy, tu aplicación podría tener fugas de memoria. Usa siempre el prefijo $ para la reactividad en componentes Svelte para que Svelte maneje esto por ti.
  • Mutar objetos directamente: Si tu store contiene un objeto o array, y lo mutas directamente sin pasar por set o update (o la asignación con $), Svelte no detectará el cambio y no re-renderizará los componentes. Siempre crea una nueva instancia del objeto/array al actualizar. Por ejemplo: todos.update(t => [...t, newTodo]) en lugar de todos.update(t => { t.push(newTodo); return t; }) (aunque Svelte es lo suficientemente inteligente para detectar esto último, es una buena práctica funcional evitar la mutación directa).

✅ Conclusión

Los Svelte Stores son una característica poderosa y elegante que simplifica enormemente la gestión de estado en tus aplicaciones Svelte. Al comprender y aplicar los conceptos de writable, readable y derived stores, junto con las buenas prácticas, estarás bien equipado para construir aplicaciones reactivas, escalables y fáciles de mantener.

La magia del $, la simplicidad de la interfaz y la eficiencia inherente de Svelte hacen que la gestión de estado sea una alegría, no una carga. ¡Ahora, sal y construye algo asombroso con Svelte Stores!

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!