SVELTE, usando el Store

Stores en Svelte

Bienvenidos a la última entrega del curso de Svelte, donde aprenderemos a usar el Store y completaremos el código de nuestro pequeño proyecto que hemos ido realizando en las entregas anteriores.

Para continuar con el curso puedes acceder al repositorio en Github y pulsando aquí puedes ver el proyecto en funcionamiento.

A continuación el índice del curso para recordar todo lo que hemos aprendido:

  1. Svelte, el nuevo framework del barrio
  2. Svelte, creando componentes
  3. Svelte, sistema de rutas
  4. Svelte, usando el Store

Que es un Store

Si en algún momento has usado otro framework de Javascript, el tema del Store te resultara familiar.

Bien, de una forma simple podemos definir que un Store es un sitio al cual se tiene acceso desde cualquier parte de nuestra aplicación pudiendo leer y modificar su contenido.

Para ello Svelte nos ofrece los siguientes métodos:

  • Writable: Modificar y leer el store.
  • Readable: Leer el store.

Ya sabemos como manipular el store en Svelte, así que ahora vamos a continuar con nuestro proyecto añadiendo esta característica.

Continuando el proyecto

Lo primero que haremos será crear un archivo de Javascript llamado store.js.

Como aclaración, no es necesario llamar al fichero store, pero por convención y la finalidad he decidido llamarlo así.

A continuación un ejemplo de uso:

store.js

import { writable } from 'svelte/store';

const dataStore = writable(0);

export default dataStore;

App.svelte

<script>
   ...
   import dataStore from './store';
</script>
<main>
   ...
   <div class="card">
      <h1>STORE</h1>
      <hr>
      Svelte store { $dataStore }
      <hr>
   </div>
</main>

El código anterior es el mínimo necesario para hacer uso del store, y es la continuación del proyecto que se encuentra en Github.

Esta es la forma más sencilla de tener acceso al store, y para ello usaremos el prefijo ‘$‘ seguido del nombre del store.

Ahora veremos otra forma de usarlo realizando una suscripción y desuscripción al store.

Subscribe / unsubscribe

Como habíamos indicando anteriormente, svelte nos ofrece diferentes formas de usar el store y la anterior es la más simple ya que solo necesitaremos añadir el prefijo ‘$‘.

Pero…, ¿ que ocurre si queremos hacer algo mas con el store ?

Para resolver esa cuestión, svelte nos ofrece otros dos métodos más:

  • subcribe: Para suscribirse a un store.
  • unsubcribe: Para desuscribirse de un store.

A continuación un ejemplo de uso:

<script>
  import { onDestroy } from 'svelte';
  import dataStore from '../store';
  let value = null;
  // Suscripción
  const unsubStore = dataStore.subscribe( data => value = data);
  // Desuscripción
  onDestroy(unsubStore);
</script>
<main class="component-store">
  <p>Valor del store dentro del componente {value}</p>
</main>
<style>
.component-store p {
  box-shadow: 0 0 9px 1px rgba(0,0,0,0.3);
  padding: 10px;
  width: 200px;
  margin: 0 auto;
}
</style>

Te preguntaras, ¿y cual es la diferencia que existe entre ambos?.

Usando el prefijo ‘$‘ la suscripción y desuscripción se realiza automáticamente y en la segunda opción la tenemos que realizar nosotros.

En este punto pensaras en usar siempre la primera opción ya que es más optima, pero en la segunda opción cuando nos suscribirnos usamos una función, y dentro podemos añadir más código.

Como podemos observar  la diferencia es importante ya que ofrece la posibilidad de realizar otras acciones pero, tenemos que recordar siempre realizar la desuscripción.

Ya sabemos como leer el contenido del store, ahora veremos como modificar el valor.

Modificando el store

Svelte nos ofrece diferentes formas de modificar el store:

  • update: Modificamos el store usando una función.
  • set: Modificamos el store usando un valor directo.

Como podemos observar las dos formas modifican el valor del store, la diferencia es que mientras en el update podemos realizar otras operaciones, el set realiza una modificación directa.

store.js

import { writable } from 'svelte/store';

export const dataStore = writable(0);

export const personalData = writable({
  data1: '',
  data2: '',
  data3: {
    data31: '',
    data32: {
      data321: '',
    },
  },
});

changeStore.svelte

<script>
  import { onDestroy } from 'svelte';
  import { dataStore, personalData } from '../store';
  let value = null;
  const unsubStore = dataStore.subscribe( data => value = data);
  onDestroy(unsubStore);
  const suma = () => dataStore.update(value => value + 1);
  const resta = () => dataStore.update(value => value - 1);
  const obj = () => personalData.update(data => {
    const nRand = Math.floor(Math.random() * 100);
    data.data1 = nRand;
    data.data2 = nRand;
    return data;
  });
</script>
<main class="component-store">
  <p>Valor del store dentro del componente {value}</p>
  <button on:click={suma}>Suma 1 Update</button>
  <button on:click={resta}>Resta 1 Update</button>
  <button on:click={obj}>Random Object Update</button>
</main>
<style>
.component-store p {
  box-shadow: 0 0 9px 1px rgba(0,0,0,0.3);
  padding: 10px;
  width: 200px;
  margin: 0 auto;
}
</style>

App.svelte

<script>
   ...
   // Usamos set para modificar el store
   const suma = value => dataStore.set(value + 1);
   const resta = value => dataStore.set(value - 1);
   
   import PsButton from './components/button.svelte';
   import Router from 'svelte-spa-router';
   import { link, push } from 'svelte-spa-router';
   import routes from './routes';
   import { dataStore, personalData } from './store';

   // Dentro del componente PsChangeStore usamos update para modificar el store
   import PsChangeStore from './components/changeStore.svelte';
</script>
<main>
   ...
   <div class="card">
      <h1>STORE</h1>
      <hr>
      Svelte store principal { $dataStore }
      <button on:click={() => suma($dataStore)}>Suma 1 Set</button>
      <button on:click={() => resta($dataStore)}>Resta 1 Set</button>
      <hr>
      <PsChangeStore />
      {JSON.stringify($personalData)}
      <hr>
   </div>
</main>

En el ejemplo anterior se ha usado tanto set como update para modificar el contenido del store.

Para finalizar el tutorial de Svelte, veremos como unificar el código del store mediante store personalizados y un ejemplo usando el método readable.

Personalizando el store

En el siguiente paso usaremos el store general, tanto para los datos como la lógica.

Para ello vamos a modificar el método suma que se encuentra tanto en el componente como en la página principal, y lo crearemos en el store.

store.js

import { writable } from 'svelte/store';

export const dataStore = writable(0);

export const personalData = writable({
  data1: '',
  data2: '',
  data3: {
    data31: '',
    data32: {
      data321: '',
    },
  },
});

const cStore = () => {
  return {
    suma: (newValue) => dataStore.update(value => value + 1),
    resta: (newValue) => dataStore.update(value => value - 1),
  }
}

export const customStore = cStore();

App.svelte

<script>
   ....
   import { dataStore, personalData, customStore } from './store';
   ....
</script>
<main>
   ...
   <div class="card">
      <h1>STORE</h1>
      <hr>
      Svelte store principal { $dataStore }
      <button on:click={() => suma($dataStore)}>Suma 1 Set</button>
      <button on:click={() => resta($dataStore)}>Resta 1 Set</button>
      <hr>
      <PsChangeStore />
      {JSON.stringify($personalData)}
      <hr>
      Método en el store (Store personalizado)
      <button on:click={() => customStore.suma($dataStore)}>Suma custom store Update</button>
      <button on:click={() => customStore.resta($dataStore)}>Resta custom store Update</button>
      <hr>
   </div>
</main>

Con el código anterior hemos comprobado que no es necesario declarar el método suma en cada uno de los sitios donde necesitemos usarlo, bastaría con crear la lógica en el store.

Ya solo nos quedaría el método readable, que tiene una sintaxis similar a writeable.

A continuación algunos ejemplos para usar readable.

store.js

import { readable, writable } from 'svelte/store';

export const dataStore = writable(0);

export const personalData = writable({
  data1: '',
  data2: '',
  data3: {
    data31: '',
    data32: {
      data321: '',
    },
  },
});

const cStore = () => {
  return {
    suma: (newValue) => dataStore.update(value => value + 1),
    resta: (newValue) => dataStore.update(value => value - 1),
  }
}

export const customStore = cStore();

const getDate = () => {
  const options = {
    year: 'numeric',
    month: 'numeric',
    day: 'numeric',
    hour: 'numeric',
    minute: 'numeric',
    second: 'numeric'
  };
  const dateTimeFormat = new Intl.DateTimeFormat('es-ES', options);
  return dateTimeFormat.format(new Date());
}
export const getTime = readable(null, set => {
  set(getDate());
});

export const getRealTime = readable(null, set => {
  setInterval(() => set(getDate()), 1000);
});

App.svelte

<script>
   ...
   import { dataStore, personalData, customStore, getTime, getRealTime } from './store';
   ...
</script>
<main>
   ...
   <div class="card">
      <h1>STORE</h1>
      <hr>
      Svelte store principal { $dataStore }
      <button on:click={() => suma($dataStore)}>Suma 1 Set</button>
      <button on:click={() => resta($dataStore)}>Resta 1 Set</button>
      <hr>
      <PsChangeStore />
      {JSON.stringify($personalData)}
      <hr>
      Método en el store (Store personalizado)
      <button on:click={() => customStore.suma($dataStore)}>Suma custom store Update</button>
      <button on:click={() => customStore.resta($dataStore)}>Resta custom store Update</button>
      <hr>
      Método <b>readable</b> para obtener la fecha y la hora: {$getTime}<br>
      Método <b>readable</b> para obtener la fecha y la hora en tiempo real: {$getRealTime}<br>
   </div>
</main>