Entity adapter

Keep collections normalized as ordered IDs and an entity record, with helpers that work inside your slice handlers.

Define IDs and ordering

createEntityAdapter<T> requires both selectId(entity): string and sortComparer(a, b): number. getInitialState() creates { ids: [], entities: {} }; an optional object adds extra fields. Get fresh adapter state for every store. IDs must be unique strings, including input passed to setAll.

tasks.ts
import { configureStore, createEntityAdapter, createEvent, createSlice } from "@carere/solux";

type Task = { id: string; title: string };
const tasks = createEntityAdapter<Task>({
  selectId: (task) => task.id,
  sortComparer: (a, b) => a.title.localeCompare(b.title),
});
const taskAdded = createEvent<Task>("tasks/added");
const slice = createSlice({
  initialState: tasks.getInitialState({ loading: false }),
  handlers: (builder) => builder.addHandler(taskAdded, (state, { payload }) => {
    tasks.addOne(state, payload);
  }),
});
const store = configureStore({ rootSlice: slice });
store.dispatch(taskAdded({ id: "docs", title: "Read Solux docs" }));
const selectors = tasks.getSelectors();
console.log(selectors.selectAll(store.state));

Choose the right operation

Add and set helpers recompute the ID ordering with sortComparer. There are no updateOne or upsertOne methods; use setOne with a complete entity to replace one. These helpers mutate the state they receive, so use them in handlers or on state you own.

OperationBehavior
addOne / addManyInsert new IDs. Existing entities are retained, not merged or replaced.
setOne / setManyInsert or replace whole entities by ID. Other entities remain.
setAllReplace the entire collection. Pass unique IDs; the input array is sorted in place.
removeOne / removeManyRemove the supplied string ID or IDs from both collections.
removeAllClear IDs and entities; preserve extra state fields.

Select inside a reactive scope

getSelectors() supplies selectIds, selectEntities, selectAll, selectTotal, and selectById. selectById returns undefined for a missing ID. Pass a selectState function to getSelectors to target a collection nested in a root state. Selectors are ordinary functions, not memoized computations: call them inside JSX or wrap the result in createMemo when useful.

Nested collection · continuing tasks.ts
import { combineSlices } from "@carere/solux";
import { createMemo } from "solid-js";

const nestedStore = configureStore({
  rootSlice: combineSlices({
    tasks: createSlice({
      initialState: tasks.getInitialState(),
      handlers: (builder) => builder.addHandler(taskAdded, (state, { payload }) => {
        tasks.addOne(state, payload);
      }),
    }),
  }),
});
type RootState = typeof nestedStore.state;
const rootSelectors = tasks.getSelectors<RootState, RootState["tasks"]>(
  (state) => state.tasks,
);
const all = createMemo(() => rootSelectors.selectAll(nestedStore.state));