La administración del estado suele ser uno de los problemas más importantes que debe abordar al desarrollar una aplicación frontend. Este artículo analiza la administración de estado para SolidJS, que es una biblioteca para crear aplicaciones web que son pequeñas y extremadamente rápidas. Si es nuevo en Solid pero ya está familiarizado con React, es posible que desee leer primero una introducción a SolidJS .
Comencemos con los componentes básicos más fundamentales del sistema de reactividad de Solid: las señales.
Señales
Una señal representa un dato observable que rastrea automáticamente los cálculos que dependen de él. Cuando un cálculo, como un efecto o una nota, llama a la función captadora de la señal, se agregará a la lista de suscripción de la señal. Cada vez que los datos cambien, la señal notificará a todos sus suscriptores. A continuación se muestra un ejemplo simple usando una señal. Tenga en cuenta que no actualizamos los datos de una señal directamente, sino que necesitamos llamar a su función de establecimiento.
import { render } from "solid-js/web" ; import { createSignal } from "solid-js" ; function Counter ( ) { const [count, setCount] = createSignal( 0 ); return ( < div >
< button onClick = {() => setCount((c) => c - 1)}> - </ button >
{count()} < button onClick = {() => setCount((c) => c + 1)}> + </ button >
</ div >
); } render( () => < Counter /> , document .getElementById( "app" ));
Es importante tener en cuenta que las señales son las unidades de cambio. Cuando actualiza cierta parte de los datos de una señal, se notifica a todos sus suscriptores sin importar si usan esa parte en particular o no. Demostremos este comportamiento con un ejemplo artificial:
En este ejemplo, tenemos una señal que utilizan dos efectos: uno representa y actualiza la interfaz de usuario, mientras que el otro imprime el apellido en la consola. El segundo efecto no usa el primer nombre, pero cada vez que cambia el primer nombre, aún se vuelve a ejecutar innecesariamente. Para evitar cálculos innecesarios, podríamos usar una señal para el nombre y otra señal para el apellido. En casos más complejos, podríamos anidar señales, por ejemplo, tener una señal para contener una lista cuyos elementos también sean señales. Funciona, pero el código sería bastante engorroso. Afortunadamente, Solid tiene una solución integrada para la reactividad anidada: las tiendas.
Historias
Usemos una tienda para reemplazar la señal en el ejemplo anterior. Ahora debería ver que el efecto que se imprime en la consola solo se vuelve a ejecutar cuando cambia el apellido, no cuando cambia el nombre.
¿Cómo funciona una tienda? Una tienda es un objeto proxy cuyas propiedades se envuelven automáticamente en los propios proxies. Detrás de escena, Solid crea perezosamente señales para propiedades a las que se accede bajo alcances de seguimiento. Básicamente, una tienda es un árbol de señales que se rastrean y modifican de forma independiente. Como probablemente haya notado en el ejemplo, la sintaxis para leer y escribir datos con tiendas es diferente a la de las señales. Para leer datos, no necesita una función getter, sino que simplemente puede acceder a las propiedades como lo haría con los objetos normales. Para escribir datos, puede usar la sintaxis de ruta de Solid Store. A continuación se muestran algunos ejemplos de la sintaxis de ruta copiada de la documentación de Solid:
const [state, setState] = createStore({ todos : [ { task : 'Finish work' , completed : false } { task : 'Go grocery shopping' , completed : false } { task : 'Make dinner' , completed : false } ] }); setState( 'todos' , [ 0 , 2 ], 'completed' , true ); // {
// todos: [
// { task: 'Finish work', completed: true }
// { task: 'Go grocery shopping', completed: false }
// { task: 'Make dinner', completed: true }
// ]
// }
setState( 'todos' , { from : 0 , to : 1 }, 'completed' , c => !c); // {
// todos: [
// { task: 'Finish work', completed: false }
// { task: 'Go grocery shopping', completed: true }
// { task: 'Make dinner', completed: true }
// ]
// }
setState( 'todos' , todo => todo.completed, 'task' , t => t + '!' ) // {
// todos: [
// { task: 'Finish work', completed: false }
// { task: 'Go grocery shopping!', completed: true }
// { task: 'Make dinner!', completed: true }
// ]
// }
setState( 'todos' , {}, todo => ({ marked : true , completed : !todo.completed })) // {
// todos: [
// { task: 'Finish work', completed: true, marked: true }
// { task: 'Go grocery shopping!', completed: false, marked: true }
// { task: 'Make dinner!', completed: false, marked: true }
// ]
// }
Si bien puedo ver que la sintaxis es poderosa, no parece muy intuitiva. Afortunadamente, Solid también ofrece un par de alternativas para actualizar las tiendas. La primera alternativa es usar una tienda mutable llamando
createMutable()
en vez de
createStore()
. De esta manera, puede actualizar la tienda como lo haría con un objeto JavaScript normal:
const store = createMutable({ firstName : "John" , lastName : "Doe" }); // read value
store.firstName // set value
store.firstName = anotherValue
Si bien el uso de una tienda mutable es muy simple, podría ser difícil razonar sobre cuándo se realizan cambios desde muchos lugares de la aplicación. Por lo tanto, recomendaría la segunda alternativa, que es usar una función de utilidad inspirada en Immer llamada
produce()
. Esta utilidad nos permite escribir código que muta los datos de forma normal pero crea automáticamente copias inmutables entre bastidores.
const store = createStore({ firstName : "John" , lastName : "Doe" }); // read value
store.firstName store.lastName // set value
setState(produce( s => { s.firstName = anotherFirstName s.lastName = anotherLastName }))
Bibliotecas Estatales
Ahora que tenemos una comprensión básica de las opciones integradas de Solid para la gestión del estado, hay una pregunta interesante que hacer: ¿necesitamos un contenedor de estado externo similar a Redux o MobX para las aplicaciones de Solid? Para responder a esa pregunta, primero debemos comprender las razones por las que necesitamos bibliotecas de estado para las aplicaciones React. En mi opinión, hay dos razones principales: separar la gestión de estado de la lógica de presentación y facilitar el intercambio de estado entre componentes en diferentes partes de la interfaz de usuario. Ninguno de los dos motivos es aplicable a Solid. Los almacenes sólidos generalmente se crean fuera del árbol de componentes, por lo que no se mezclan con la lógica de presentación. Y puede importar y usar una tienda desde cualquier componente, por lo que compartir el estado tampoco es un problema. Eso significa que generalmente no hay razón para usar un contenedor de estado externo en una aplicación sólida. Sin embargo, creo que una biblioteca de estado que también maneja la obtención de datos (similar a React Query o SWR) aún podría ser útil porque no tendría que preocuparse por la estructura del estado de la aplicación, además la biblioteca puede mantener los datos en caché actualizados automáticamente. .
Conclusión
Este artículo ha presentado las dos opciones integradas para la gestión de estado en una aplicación de Solid. Usa señales para datos atómicos. Use una tienda si el estado es una estructura compleja cuyas partes se pueden modificar de forma independiente. Utilice la sintaxis de ruta o la
produce()
Utilidad para actualizar tiendas. No necesita una biblioteca estatal como Redux o MobX. Para la mayoría de las aplicaciones, el uso de una o más tiendas para administrar el estado de la aplicación debería ser suficiente.