TState is a simple, framework-agnostic state management library written in TypeScript. It also comes with a wrapper function that turns stores into React hooks.
There are mainly two big state management libraries for React: Redux and MobX. But they are either very complex or difficult to use with TypeScript. There's also Unstaded which is quite similar to TState but is slightly different to use.
I've created TState with these goals in mind:
- As easy to use as possible
- Make use of TypeScript's type inference as much as possible
- Still make it quite feature-rich so it can be used without additional libraries
- Basic store functionality: create, read and update the state
- Computed values that get updated automatically
- Reset one store or all stores to their default values
- Subscribe to all changes within a store or filter changes by value
- A store must be a plain object. Arrays and other types result in undefined behavior. The object's properties can by of any type though.
$ npm install @alinnert/tstate
This handles the usage of version 2 which hasn't been released yet. It's coming soon.
First, create the types and interfaces for your store:
export enum Status {
ok,
loading,
error
}
interface News {
title: string
body: string
}
interface NewsStore {
items: News[]
status: Status
}
Create the store by defining its default values and (optionally) computed values:
import { createStore } from '@alinnert/tstate'
// ...
const store = createStore(
(): NewsStore => ({
items: [],
status: Status.ok
}),
{
computed: {
itemsCount: state => state.items.length
}
}
)
If you're using TState with react create your hook:
import { createStore, createReactHook } from '@alinnert/tstate'
// ...
const store = createStore(/* ... */)
export const useNewsStore = createReactHook(store)
Make sure the hook's name starts with use
so ESLint knows it's a React hook.
Now you can use your store in your components:
import { useNewsStore, Status } from './newsStore.ts'
export function NewsList() {
const { items, status, itemsCount } = useNewsStore()
return (
<div>
<p>There are {itemsCount} articles.</p>
{status === Status.loading ? (
<div>Articles are being loaded...</div>
) : null}
{items.map(item => (
<article>
<header>
<h2>{item.title}</h2>
<div>{item.body}</div>
</header>
</article>
))}
</div>
)
}
If you're not using React you can access the data in your store with store.state
. E.g. store.state.items
To update a store's state you can use the .set()
method. I recommend to export functions from your store that handles all the updating:
const store = createStore(/* ... */)
export async function loadArticles() {
store.set({ status: Status.loading })
try {
const result = await fetch(/* ... */)
const items = await result.json()
store.set({ status: Status.ok, items })
} catch (error) {
store.set({ status: Status.error })
}
}
To reset a store you can call the .reset()
function. It will set all values to the ones you defined in the initialization function that you passed to createStore()
:
store.reset()
To reset all available stores there's a resetAllStores()
function:
import { resetAllStores } from '@alinnert/tstate'
resetAllStores()
To react to changes in your store you can call .subscribe(name)
or .subscribeAll()
on your store.
.subscribe(name)
reacts to changes of a specific value. You can react to normal and computed values:
store.subscribe('items', state => {
console.log(
`New articles arrived. Now there are ${state.itemsCount} articles.`
)
})
.subscribeAll()
reacts to any change in a store. createReactHook()
uses this one internally:
store.subscribeAll(state => {
console.log('Something happend in the store.')
})