@inglorious/react-store
v17.0.0
Published
Official React bindings for @inglorious/store. Provides hooks and a Provider to connect your React components to the store.
Maintainers
Readme
@inglorious/react-store
Official React bindings for @inglorious/store.
Connect your React app to Inglorious Store with a familiar API. Built on react-redux for rock-solid performance and compatibility.
Features
- Drop-in Integration: Works just like
react-reduxwith enhanced features for Inglorious Store - Custom
useNotifyHook: Dispatch events with a clean, ergonomic API - Convenience
useEntityHook: Select a single entity by its ID with a simple, optimized hook. - Battle-tested: Built on
react-reduxfor proven performance and stability - TypeScript Support: Optional type safety for those who want it
Installation
npm install @inglorious/store @inglorious/react-store react react-domQuick Start
1. Create Your Store
// store.js
import { createStore } from "@inglorious/store"
const types = {
counter: {
increment(counter) {
counter.value++
},
decrement(counter) {
counter.value--
},
},
}
const entities = {
myCounter: { type: "counter", value: 0 },
}
export const store = createStore({ types, entities })2. Set Up React Bindings
// react-store.js
import { createReactStore } from "@inglorious/react-store"
import { store } from "./store"
export const { Provider, useSelector, useNotify, useEntity } =
createReactStore(store)3. Wrap Your App
// App.jsx
import { Provider } from "./react-store"
function App() {
return (
<Provider>
<Counter />
</Provider>
)
}4. Use in Components
// Counter.jsx
import { useNotify, useEntity } from "./react-store"
function Counter() {
const notify = useNotify()
const { value } = useEntity("myCounter")
return (
<div>
<h1>Count: {value}</h1>
<button onClick={() => notify("increment")}>+</button>
<button onClick={() => notify("decrement")}>-</button>
</div>
)
}API Reference
createReactStore(store)
Creates React bindings for an Inglorious Store.
Parameters:
store(required): An Inglorious Store instance
Returns:
Provider: React context provider component (pre-configured with your store)useSelector: Hook to select state slicesuseNotify: Hook to dispatch eventsuseEntity: Hook to select a single entity by ID
Examples:
const { Provider, useSelector, useNotify, useEntity } = createReactStore(store)useNotify()
Hook that returns a function to dispatch events.
Returns:
notify(type, payload?): Function to dispatch events
Usage:
function TodoItem({ id }) {
const notify = useNotify()
// Simple event
const handleToggle = () => notify("toggleTodo", id)
// Event with complex payload
const handleRename = (text) => notify("renameTodo", { id, text })
return (
<div>
<button onClick={handleToggle}>Toggle</button>
<input onChange={(e) => handleRename(e.target.value)} />
</div>
)
}useSelector(selector, equalityFn?)
Hook to select and subscribe to state slices. Works exactly like react-redux's useSelector.
Parameters:
selector: Function that receives state and returns a sliceequalityFn(optional): Custom equality function for optimization
Usage:
function TodoList() {
// Select a specific entity
const task = useSelector((state) => state.task1)
// Select derived data
const completedCount = useSelector(
(state) => Object.values(state).filter((task) => task.completed).length,
)
// With custom equality
const tasks = useSelector(
(state) => state.tasks,
(prev, next) => prev === next, // Shallow equality
)
return <div>...</div>
}useEntity(id)
A convenience hook to select a single entity by its ID. It is an optimized way to subscribe a component to updates for a specific entity.
Parameters:
id(required): The ID of the entity to select.
Usage:
function UserProfile() {
const user = useEntity("user-123")
if (!user) {
return "User not found."
}
return user.name
}Redux DevTools Integration
Redux DevTools work automatically! Install the browser extension to inspect state snapshots, event history, and use time-travel debugging.
The underlying @inglorious/store allows for configuration, such as skipping certain events from being logged. See the store documentation for more details.
Complete Example: Todo App
// store.js
import { createStore } from "@inglorious/store"
const types = {
form: {
inputChange(entity, value) {
entity.value = value
},
formSubmit(entity) {
entity.value = ""
},
},
list: {
formSubmit(entity, value) {
entity.tasks.push({
id: Date.now(),
text: value,
completed: false,
})
},
toggleClick(entity, id) {
const task = entity.tasks.find((t) => t.id === id)
if (task) task.completed = !task.completed
},
},
}
const entities = {
form: { type: "form", value: "" },
list: { type: "list", tasks: [] },
}
export const store = createStore({ types, entities })// react-store.js
import { createReactStore } from "@inglorious/react-store"
import { store } from "./store"
export const { Provider, useSelector, useNotify } = createReactStore(store)// App.jsx
import { Provider } from "./react-store"
import TodoApp from "./TodoApp"
export default function App() {
return (
<Provider>
<TodoApp />
</Provider>
)
}// TodoApp.jsx
import { useNotify, useSelector } from "./react-store"
export default function TodoApp() {
const notify = useNotify()
const formValue = useSelector((state) => state.form.value)
const tasks = useSelector((state) => state.list.tasks)
const handleSubmit = (e) => {
e.preventDefault()
notify("formSubmit", formValue)
}
return (
<div>
<form onSubmit={handleSubmit}>
<input
value={formValue}
onChange={(e) => notify("inputChange", e.target.value)}
placeholder="Add a task"
/>
<button type="submit">Add</button>
</form>
<ul>
{tasks.map((task) => (
<li key={task.id}>
<input
type="checkbox"
checked={task.completed}
onChange={() => notify("toggleClick", task.id)}
/>
<span
style={{
textDecoration: task.completed ? "line-through" : "none",
}}
>
{task.text}
</span>
</li>
))}
</ul>
</div>
)
}TypeScript Support
Full TypeScript support is available! The library includes complete type definitions.
Quick example:
// Define your types
import { BaseEntity } from "@inglorious/store"
interface CounterEntity extends BaseEntity {
type: "counter"
value: number
}
interface AppState {
myCounter: CounterEntity
[id: string]: CounterEntity
}
// Create typed store
const store = createStore<CounterEntity, AppState>({ types, entities })
// Everything else works the same!
const { Provider, useSelector, useNotify } = createReactStore(store)For complete TypeScript examples, see the @inglorious/store TypeScript documentation.
FAQ
Q: Can I use this with existing react-redux code?
A: Yes! The Provider and useSelector are compatible. You can gradually migrate to useNotify.
Q: Does this work with React Native?
A: Yes! It works anywhere react-redux works.
Q: Can I use Redux middleware?
A: Use Inglorious Store middleware instead. See @inglorious/store docs.
Q: Do I need TypeScript?
A: Not at all! The library works great with plain JavaScript. TypeScript support is completely optional.
Comparison to Plain react-redux
What's the same:
<Provider>anduseSelectorwork identically- Full Redux DevTools support
- Same performance characteristics
What's different:
- ✅
useNotifyhook for dispatching events instead ofuseDispatch - ✅
useEntityhook for easily selecting an entity by ID - ✅ Cleaner API for event-based state management
License
MIT License - Free and open source
Created by Matteo Antony Mistretta
You're free to use, modify, and distribute this software. See LICENSE for details.
Contributing
Contributions welcome! Please read our Contributing Guidelines first.
