with one click
refrakt
How to use Refrakt signals-based state management library
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
How to use Refrakt signals-based state management library
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
| name | refrakt |
| description | How to use Refrakt signals-based state management library |
Refrakt is a lightweight state management library built on TC39 signals.
refrakt — curated entry point: re-exports the primary constructors and core typesrefrakt/signal.js — signal primitives: signal, computed, effect, untrackrefrakt/reducer.js — pure reducer-based state: reducer, withLoggingrefrakt/store.js — transactional state with effects: store, tx, noFx, withLoggingrefrakt/send.js — action utilities: forwardrefrakt/scope.js — scoped child stores: scoperefrakt/iter.js — async iterator utilities: mergeAsync, sequenceAsync, mapAsyncrefrakt/never.js — exhaustiveness helpers: assertNever, NeverErrorUse reducer when you only need state transitions with no side effects.
import { type Reducer, reducer } from "refrakt/reducer.js";
import { assertNever } from "refrakt/never.js";
type Action = { type: "increment" } | { type: "decrement" };
const update: Reducer<number, Action> = (state, action) => {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
return assertNever(action);
}
};
const counter = reducer(update, 0);
counter.send({ type: "increment" });
counter.get(); // 1
Use store when you need side effects. The update function returns a transaction via tx(state, fx?). Effects are async generators that yield actions back to the store. The state() callback passed to fx reads current store state — use it to check flags and cancel effects.
import { type Update, store, tx } from "refrakt/store.js";
import { assertNever } from "refrakt/never.js";
type Model = { isRunning: boolean; elapsed: number };
type Action = { type: "start" } | { type: "stop" } | { type: "tick" };
const update: Update<Model, Action, void> = (state, action) => {
switch (action.type) {
case "start":
return tx(
{ ...state, isRunning: true } as Model,
async function* (state) {
while (state().isRunning) {
await delay(1000);
if (state().isRunning) {
yield { type: "tick" };
}
}
},
);
case "stop":
return tx({ ...state, isRunning: false } as Model);
case "tick":
return tx({ ...state, elapsed: state.elapsed + 1 } as Model);
default:
return assertNever(action);
}
};
const myStore = store(update, { isRunning: false, elapsed: 0 });
Pass a third argument to store for dependency injection:
const update: Update<Model, Action, { api: Api }> = (
state,
action,
context,
) => {
switch (action.type) {
case "fetch":
return tx({ ...state, loading: true } as Model, async function* () {
const data = await context.api.getData();
yield { type: "set", data };
});
// ...
}
};
const myStore = store(update, initialState, { api: myApi });
import { signal, computed, effect, untrack } from "refrakt/signal.js";
const count = signal(0);
const doubled = computed(() => count.get() * 2);
// Effects are batched to microtask
const cleanup = effect(() => {
console.log(count.get());
});
// Read without tracking
const value = untrack(() => count.get());
Create a child store that projects a subset of parent state and maps actions:
import { scope } from "refrakt/scope.js";
const child = scope({
store: parentStore,
get: (state) => state.child,
tag: (action) => ({ type: "child", action }),
});
Transform a send function to tag actions:
import { forward } from "refrakt/send.js";
const sendChild = forward(parentStore.send, (action) => ({
type: "child",
action,
}));
assertNever(action) (from refrakt/never.js) in the default arm. TypeScript will error if the switch isn't exhaustive.state() inside fx to read current state. Check a flag (e.g. isRunning) to break out of loops.as Model assertions: Use as Model when spreading state in tx() calls to help TypeScript infer the transaction type.