generic-redux-store
v0.0.5
Published
Generic Redux Store makes updating Redux state as simple as editing a normal JavaScript object. You register slices at runtime and receive proxies that intercept property assignments, array mutations and deletions. These proxies batch your changes and dis
Readme
Generic Redux Store
Generic Redux Store makes updating Redux state as simple as editing a normal JavaScript object. You register slices at runtime and receive proxies that intercept property assignments, array mutations and deletions. These proxies batch your changes and dispatch thunk actions automatically using requestAnimationFrame (with microtask and setTimeout fallbacks). React hooks expose the same API and flush pending updates when components unmount.
Features
- Update store objects just by mutating a proxy—no manual
dispatchcalls. - Thunk dispatching and batching are handled transparently.
- Array methods and
deleteoperations are supported. - Register slices dynamically with strong TypeScript inference.
- React hooks return the same proxies and auto-flush on unmount.
Quick Start
Prerequisites
- Node.js 18+
- npm
Installation
npm installBuild
npm run buildCompiles the TypeScript sources to JavaScript under dist/.
Using the library
Install the package (named generic-redux-store) and import the compiled output:
npm install generic-redux-storeimport { Stores } from 'generic-redux-store/dist/index.js';redux, react, and react-redux are required peer dependencies.
Running tests
npm testTests run with Vitest and tests/setup.ts provides a small requestAnimationFrame polyfill for Node environments.
API Overview
The main entry point is the Stores class.
Stores.create(id)– start a new builder for a store.Stores.get(id)– retrieve a previously created builder.Stores.destroy(id)– remove a builder and all reducers so the ID can be reused.addOne(key, defaultState)– register a single-entity slice.addMany(key, defaultState)– register a multi-entity slice.build()– finalize the store and return the configured slices.
Each slice exposes methods:
| Method | Purpose |
| ------------------------------ | ----------------------------------- |
| select / useSelect | Access a single-entity proxy. |
| selectById / useSelectById | Access an entity proxy by ID. |
| selectAll / useSelectAll | Get all entities as a proxy object. |
| getActions | Access the underlying RTK actions. |
All returned proxies implement flush() to commit queued operations immediately.
Usage Scenarios
Single entity slice
const builder = Stores.create('settings').addOne('config', {
theme: 'light',
options: { fontSize: 16 },
});
const store = builder.build();
store.config.select().options.fontSize = 18;
store.config.select().flush();Multiple entity slice
const builder = Stores.create('users').addMany('items', {
name: '',
roles: [],
});
const store = builder.build();
store.items.selectById('u1').name = 'Alice';
store.items.selectById('u1').roles.push('admin');
// requestAnimationFrame batches updates. In tests a polyfill provides it,
// otherwise `.flush()` can be called directly.
await new Promise(requestAnimationFrame); // or call .flush()Array operations
All array methods and length assignments are intercepted.
const roles = store.items.selectById('u1').roles;
roles.push('editor');
roles.pop();
roles.flush();Deletion
Properties or entities can be removed via the proxies.
delete store.items.selectById('u1').name;
delete store.items.selectAll()['u1'];
store.items.selectAll().flush();Manual vs automatic flushing
Operations queue until the next animation frame. Call flush() if synchronous commits are needed.
If requestAnimationFrame is not defined a microtask will run the flush, with
setTimeout used only as a final fallback.
React hooks
Slices provide hooks that return the same proxy interface.
function Item({ id }: { id: string }) {
const user = store.items.useSelectById(id);
useEffect(() => {
user.roles.push('viewer');
return () => user.flush(); // ensure pending ops are applied on unmount
}, [user]);
return <span>{user.name}</span>;
}Unmounting a component automatically flushes pending operations.
useSelectAll
useSelectAll lets a component subscribe to every entity in a slice. It returns
the same proxy type along with flush(), and automatically flushes queued
changes on unmount.
function UserList() {
const users = store.items.useSelectAll();
useEffect(() => {
users['u1'].roles.push('manager');
return () => users.flush();
}, [users]);
return (
<ul>
{Object.values(users).map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}Pending updates follow the Manual vs automatic
flushing rules. Call flush() before invoking
Stores.destroy() to ensure all writes are committed.
Testing & Development
The tests/ directory contains suites covering nested updates, array methods, deletions, iteration, and React hook cleanup. Run linting and tests before contributing:
npm run lint
npm testLicense
This project is licensed under the MIT License.
