Inicio rápido
Instalación
npm install @violetflux/kerros pnpm add @violetflux/kerros yarn add @violetflux/kerros bun add @violetflux/kerros
Kerros funciona con npm, pnpm, Yarn y Bun, y es compatible con React 17, 18 y 19.
Crear un Store
Cualquier custom Hook puede convertirse en estado compartido al envolverlo con createStore.
import { createStore } from '@violetflux/kerros'
import { useState } from 'react'
interface Task {
id : string
title : string
}
function useTaskStoreValue () {
const [ tasks , setTasks ] = useState < Task []>([])
const addTask = (task : Task ) => {
setTasks (v => [ ... v , task])
}
const finishTask = (taskId : string ) => {
setTasks (v => v .filter (task => task .id !== taskId))
}
return { tasks , addTask , finishTask }
}
export const [ useTask , TaskProvider ] = createStore (useTaskStoreValue)
createStore devuelve el Hook que usan los componentes y su Provider correspondiente. Dentro del Store puedes seguir usando useState, useReducer, Context, Hooks de SDK o tus propios Hooks.
Mantén el initializer como un Hook con nombre en el nivel superior, por ejemplo useTaskStoreValue. Los initializers anónimos siguen funcionando, pero React Compiler no los compila automáticamente como Hooks en modo infer.
Montar el Provider
useTask solo puede llamarse dentro de los descendientes de TaskProvider.
function App () {
return (
< TaskProvider >
< Header />
< TaskList />
</ TaskProvider >
)
}
Fuera del Provider, Kerros lanza un error claro.
Usar el Store en un componente
El selector devuelve únicamente los campos que utiliza el componente.
function TaskList () {
const { tasks , finishTask } = useTask (s => ({
tasks : s .tasks ,
finishTask : s .finishTask ,
}))
return (
< ul >
{ tasks .map (task => (
< li key = { task .id}>
{ task .title}
< button onClick = {() => finishTask ( task .id)}>Terminar</ button >
</ li >
))}
</ ul >
)
}
Kerros compara superficialmente los campos superiores del objeto selector. Mientras esos campos seleccionados sigan siendo iguales, otras actualizaciones del Store no vuelven a renderizar TaskList. El selector puede escribirse en línea y no necesita useCallback.
Varias instancias
Cada Provider montado crea un Store independiente.
< TaskProvider >
< h2 >Tareas personales</ h2 >
< TaskList />
</ TaskProvider >
< TaskProvider >
< h2 >Tareas del equipo</ h2 >
< TaskList />
</ TaskProvider >
Cada TaskList lee automáticamente el Provider más cercano. Los datos permanecen aislados.
Dependencias entre Stores
Un Store puede llamar directamente a otro Store.
function useTaskStoreValue () {
const { user } = useAccount (s => ({ user : s .user }))
const [ tasks , setTasks ] = useState < Task []>([])
const addTask = (title : string ) => {
if ( ! user)
return
setTasks (v => [ ... v , {
id : crypto .randomUUID () ,
title ,
assigneeId : user .id ,
}])
}
return { tasks , addTask }
}
export const [ useTask , TaskProvider ] = createStore (useTaskStoreValue)
Monta el Provider de la dependencia por fuera y mantén la dependencia en una sola dirección.
< AccountProvider >
< TaskProvider >
< App />
</ TaskProvider >
</ AccountProvider >
Pasar props al Provider
Las props del Provider se pasan al Hook del Store.
function useCounterStoreValue ({ initialCount } : { initialCount : number }) {
const [ count , setCount ] = useState (initialCount)
return { count , setCount }
}
const [ useCounter , CounterProvider ] = createStore (useCounterStoreValue)
< CounterProvider initialCount = { 42 }>
< Counter />
</ CounterProvider >
Probarlo
Este contador utiliza la misma API createStore + Provider + selector.
Count: 0
Continúa con Selectores y Composición de Stores .