@inglorious/react-store 10.0.1 → 11.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +405 -405
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,405 +1,405 @@
|
|
|
1
|
-
# @inglorious/react-store
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/@inglorious/react-store)
|
|
4
|
-
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
|
|
6
|
-
Official React bindings for **[@inglorious/store](https://www.npmjs.com/package/@inglorious/store)**.
|
|
7
|
-
|
|
8
|
-
Connect your React app to Inglorious Store with a familiar API. Built on `react-redux` for rock-solid performance and compatibility.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Features
|
|
13
|
-
|
|
14
|
-
- **Drop-in Integration**: Works just like `react-redux` with enhanced features for Inglorious Store
|
|
15
|
-
- **Custom `useNotify` Hook**: Dispatch events with a clean, ergonomic API
|
|
16
|
-
- **Convenience `useEntity` Hook**: Select a single entity by its ID with a simple, optimized hook.
|
|
17
|
-
- **Battle-tested**: Built on `react-redux` for proven performance and stability
|
|
18
|
-
- **TypeScript Support**: Optional type safety for those who want it
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npm install @inglorious/store @inglorious/react-store react react-dom
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## Quick Start
|
|
31
|
-
|
|
32
|
-
### 1. Create Your Store
|
|
33
|
-
|
|
34
|
-
```javascript
|
|
35
|
-
// store.js
|
|
36
|
-
import { createStore } from "@inglorious/store"
|
|
37
|
-
|
|
38
|
-
const types = {
|
|
39
|
-
counter: {
|
|
40
|
-
increment(counter) {
|
|
41
|
-
counter.value++
|
|
42
|
-
},
|
|
43
|
-
decrement(counter) {
|
|
44
|
-
counter.value--
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const entities = {
|
|
50
|
-
myCounter: { type: "counter", value: 0 },
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export const store = createStore({ types, entities })
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### 2. Set Up React Bindings
|
|
57
|
-
|
|
58
|
-
```javascript
|
|
59
|
-
// react-store.js
|
|
60
|
-
import { createReactStore } from "@inglorious/react-store"
|
|
61
|
-
import { store } from "./store"
|
|
62
|
-
|
|
63
|
-
export const { Provider, useSelector, useNotify, useEntity } =
|
|
64
|
-
createReactStore(store)
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### 3. Wrap Your App
|
|
68
|
-
|
|
69
|
-
```jsx
|
|
70
|
-
// App.jsx
|
|
71
|
-
import { Provider } from "./react-store"
|
|
72
|
-
|
|
73
|
-
function App() {
|
|
74
|
-
return (
|
|
75
|
-
<Provider>
|
|
76
|
-
<Counter />
|
|
77
|
-
</Provider>
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### 4. Use in Components
|
|
83
|
-
|
|
84
|
-
```jsx
|
|
85
|
-
// Counter.jsx
|
|
86
|
-
import { useNotify, useEntity } from "./react-store"
|
|
87
|
-
|
|
88
|
-
function Counter() {
|
|
89
|
-
const notify = useNotify()
|
|
90
|
-
const { value } = useEntity("myCounter")
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<div>
|
|
94
|
-
<h1>Count: {value}</h1>
|
|
95
|
-
<button onClick={() => notify("increment")}>+</button>
|
|
96
|
-
<button onClick={() => notify("decrement")}>-</button>
|
|
97
|
-
</div>
|
|
98
|
-
)
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
## API Reference
|
|
105
|
-
|
|
106
|
-
### `createReactStore(store)`
|
|
107
|
-
|
|
108
|
-
Creates React bindings for an Inglorious Store.
|
|
109
|
-
|
|
110
|
-
**Parameters:**
|
|
111
|
-
|
|
112
|
-
- `store` (required): An Inglorious Store instance
|
|
113
|
-
|
|
114
|
-
**Returns:**
|
|
115
|
-
|
|
116
|
-
- `Provider`: React context provider component (pre-configured with your store)
|
|
117
|
-
- `useSelector`: Hook to select state slices
|
|
118
|
-
- `useNotify`: Hook to dispatch events
|
|
119
|
-
- `useEntity`: Hook to select a single entity by ID
|
|
120
|
-
|
|
121
|
-
**Examples:**
|
|
122
|
-
|
|
123
|
-
```javascript
|
|
124
|
-
const { Provider, useSelector, useNotify, useEntity } = createReactStore(store)
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### `useNotify()`
|
|
128
|
-
|
|
129
|
-
Hook that returns a function to dispatch events.
|
|
130
|
-
|
|
131
|
-
**Returns:**
|
|
132
|
-
|
|
133
|
-
- `notify(type, payload?)`: Function to dispatch events
|
|
134
|
-
|
|
135
|
-
**Usage:**
|
|
136
|
-
|
|
137
|
-
```jsx
|
|
138
|
-
function TodoItem({ id }) {
|
|
139
|
-
const notify = useNotify()
|
|
140
|
-
|
|
141
|
-
// Simple event
|
|
142
|
-
const handleToggle = () => notify("toggleTodo", id)
|
|
143
|
-
|
|
144
|
-
// Event with complex payload
|
|
145
|
-
const handleRename = (text) => notify("renameTodo", { id, text })
|
|
146
|
-
|
|
147
|
-
return (
|
|
148
|
-
<div>
|
|
149
|
-
<button onClick={handleToggle}>Toggle</button>
|
|
150
|
-
<input onChange={(e) => handleRename(e.target.value)} />
|
|
151
|
-
</div>
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### `useSelector(selector, equalityFn?)`
|
|
157
|
-
|
|
158
|
-
Hook to select and subscribe to state slices. Works exactly like `react-redux`'s `useSelector`.
|
|
159
|
-
|
|
160
|
-
**Parameters:**
|
|
161
|
-
|
|
162
|
-
- `selector`: Function that receives state and returns a slice
|
|
163
|
-
- `equalityFn` (optional): Custom equality function for optimization
|
|
164
|
-
|
|
165
|
-
**Usage:**
|
|
166
|
-
|
|
167
|
-
```jsx
|
|
168
|
-
function TodoList() {
|
|
169
|
-
// Select a specific entity
|
|
170
|
-
const task = useSelector((state) => state.task1)
|
|
171
|
-
|
|
172
|
-
// Select derived data
|
|
173
|
-
const completedCount = useSelector(
|
|
174
|
-
(state) => Object.values(state).filter((task) => task.completed).length,
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
// With custom equality
|
|
178
|
-
const tasks = useSelector(
|
|
179
|
-
(state) => state.tasks,
|
|
180
|
-
(prev, next) => prev === next, // Shallow equality
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
return <div>...</div>
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### `useEntity(id)`
|
|
188
|
-
|
|
189
|
-
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.
|
|
190
|
-
|
|
191
|
-
**Parameters:**
|
|
192
|
-
|
|
193
|
-
- `id` (required): The ID of the entity to select.
|
|
194
|
-
|
|
195
|
-
**Usage:**
|
|
196
|
-
|
|
197
|
-
```jsx
|
|
198
|
-
function UserProfile() {
|
|
199
|
-
const user = useEntity("user-123")
|
|
200
|
-
if (!user) {
|
|
201
|
-
return "User not found."
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return user.name
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
## Redux DevTools Integration
|
|
211
|
-
|
|
212
|
-
Redux DevTools work automatically! Install the browser extension to inspect state snapshots, event history, and use time-travel debugging.
|
|
213
|
-
|
|
214
|
-
The underlying `@inglorious/store` allows for configuration, such as skipping certain events from being logged. See the store documentation for more details.
|
|
215
|
-
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
## Complete Example: Todo App
|
|
219
|
-
|
|
220
|
-
```javascript
|
|
221
|
-
// store.js
|
|
222
|
-
import { createStore } from "@inglorious/store"
|
|
223
|
-
|
|
224
|
-
const types = {
|
|
225
|
-
form: {
|
|
226
|
-
inputChange(entity, value) {
|
|
227
|
-
entity.value = value
|
|
228
|
-
},
|
|
229
|
-
formSubmit(entity) {
|
|
230
|
-
entity.value = ""
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
|
|
234
|
-
list: {
|
|
235
|
-
formSubmit(entity, value) {
|
|
236
|
-
entity.tasks.push({
|
|
237
|
-
id: Date.now(),
|
|
238
|
-
text: value,
|
|
239
|
-
completed: false,
|
|
240
|
-
})
|
|
241
|
-
},
|
|
242
|
-
toggleClick(entity, id) {
|
|
243
|
-
const task = entity.tasks.find((t) => t.id === id)
|
|
244
|
-
if (task) task.completed = !task.completed
|
|
245
|
-
},
|
|
246
|
-
},
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const entities = {
|
|
250
|
-
form: { type: "form", value: "" },
|
|
251
|
-
list: { type: "list", tasks: [] },
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export const store = createStore({ types, entities })
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
```javascript
|
|
258
|
-
// react-store.js
|
|
259
|
-
import { createReactStore } from "@inglorious/react-store"
|
|
260
|
-
import { store } from "./store"
|
|
261
|
-
|
|
262
|
-
export const { Provider, useSelector, useNotify } = createReactStore(store)
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
```jsx
|
|
266
|
-
// App.jsx
|
|
267
|
-
import { Provider } from "./react-store"
|
|
268
|
-
import TodoApp from "./TodoApp"
|
|
269
|
-
|
|
270
|
-
export default function App() {
|
|
271
|
-
return (
|
|
272
|
-
<Provider>
|
|
273
|
-
<TodoApp />
|
|
274
|
-
</Provider>
|
|
275
|
-
)
|
|
276
|
-
}
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
```jsx
|
|
280
|
-
// TodoApp.jsx
|
|
281
|
-
import { useNotify, useSelector } from "./react-store"
|
|
282
|
-
|
|
283
|
-
export default function TodoApp() {
|
|
284
|
-
const notify = useNotify()
|
|
285
|
-
|
|
286
|
-
const formValue = useSelector((state) => state.form.value)
|
|
287
|
-
const tasks = useSelector((state) => state.list.tasks)
|
|
288
|
-
|
|
289
|
-
const handleSubmit = (e) => {
|
|
290
|
-
e.preventDefault()
|
|
291
|
-
notify("formSubmit", formValue)
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return (
|
|
295
|
-
<div>
|
|
296
|
-
<form onSubmit={handleSubmit}>
|
|
297
|
-
<input
|
|
298
|
-
value={formValue}
|
|
299
|
-
onChange={(e) => notify("inputChange", e.target.value)}
|
|
300
|
-
placeholder="Add a task"
|
|
301
|
-
/>
|
|
302
|
-
<button type="submit">Add</button>
|
|
303
|
-
</form>
|
|
304
|
-
|
|
305
|
-
<ul>
|
|
306
|
-
{tasks.map((task) => (
|
|
307
|
-
<li key={task.id}>
|
|
308
|
-
<input
|
|
309
|
-
type="checkbox"
|
|
310
|
-
checked={task.completed}
|
|
311
|
-
onChange={() => notify("toggleClick", task.id)}
|
|
312
|
-
/>
|
|
313
|
-
<span
|
|
314
|
-
style={{
|
|
315
|
-
textDecoration: task.completed ? "line-through" : "none",
|
|
316
|
-
}}
|
|
317
|
-
>
|
|
318
|
-
{task.text}
|
|
319
|
-
</span>
|
|
320
|
-
</li>
|
|
321
|
-
))}
|
|
322
|
-
</ul>
|
|
323
|
-
</div>
|
|
324
|
-
)
|
|
325
|
-
}
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
---
|
|
329
|
-
|
|
330
|
-
## TypeScript Support
|
|
331
|
-
|
|
332
|
-
Full TypeScript support is available! The library includes complete type definitions.
|
|
333
|
-
|
|
334
|
-
**Quick example:**
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
// Define your types
|
|
338
|
-
import { BaseEntity } from "@inglorious/store"
|
|
339
|
-
|
|
340
|
-
interface CounterEntity extends BaseEntity {
|
|
341
|
-
type: "counter"
|
|
342
|
-
value: number
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
interface AppState {
|
|
346
|
-
myCounter: CounterEntity
|
|
347
|
-
[id: string]: CounterEntity
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// Create typed store
|
|
351
|
-
const store = createStore<CounterEntity, AppState>({ types, entities })
|
|
352
|
-
|
|
353
|
-
// Everything else works the same!
|
|
354
|
-
const { Provider, useSelector, useNotify } = createReactStore(store)
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
For complete TypeScript examples, see the [@inglorious/store TypeScript documentation](https://www.npmjs.com/package/@inglorious/store#-type-safety).
|
|
358
|
-
|
|
359
|
-
---
|
|
360
|
-
|
|
361
|
-
## FAQ
|
|
362
|
-
|
|
363
|
-
**Q: Can I use this with existing react-redux code?**
|
|
364
|
-
A: Yes! The `Provider` and `useSelector` are compatible. You can gradually migrate to `useNotify`.
|
|
365
|
-
|
|
366
|
-
**Q: Does this work with React Native?**
|
|
367
|
-
A: Yes! It works anywhere `react-redux` works.
|
|
368
|
-
|
|
369
|
-
**Q: Can I use Redux middleware?**
|
|
370
|
-
A: Use Inglorious Store middleware instead. See [@inglorious/store docs](https://www.npmjs.com/package/@inglorious/store).
|
|
371
|
-
|
|
372
|
-
**Q: Do I need TypeScript?**
|
|
373
|
-
A: Not at all! The library works great with plain JavaScript. TypeScript support is completely optional.
|
|
374
|
-
|
|
375
|
-
---
|
|
376
|
-
|
|
377
|
-
## Comparison to Plain react-redux
|
|
378
|
-
|
|
379
|
-
**What's the same:**
|
|
380
|
-
|
|
381
|
-
- `<Provider>` and `useSelector` work identically
|
|
382
|
-
- Full Redux DevTools support
|
|
383
|
-
- Same performance characteristics
|
|
384
|
-
|
|
385
|
-
**What's different:**
|
|
386
|
-
|
|
387
|
-
- ✅ `useNotify` hook for dispatching events instead of `useDispatch`
|
|
388
|
-
- ✅ `useEntity` hook for easily selecting an entity by ID
|
|
389
|
-
- ✅ Cleaner API for event-based state management
|
|
390
|
-
|
|
391
|
-
---
|
|
392
|
-
|
|
393
|
-
## License
|
|
394
|
-
|
|
395
|
-
**MIT License - Free and open source**
|
|
396
|
-
|
|
397
|
-
Created by [Matteo Antony Mistretta](https://github.com/IngloriousCoderz)
|
|
398
|
-
|
|
399
|
-
You're free to use, modify, and distribute this software. See [LICENSE](./LICENSE) for details.
|
|
400
|
-
|
|
401
|
-
---
|
|
402
|
-
|
|
403
|
-
## Contributing
|
|
404
|
-
|
|
405
|
-
Contributions welcome! Please read our [Contributing Guidelines](../../CONTRIBUTING.md) first.
|
|
1
|
+
# @inglorious/react-store
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@inglorious/react-store)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Official React bindings for **[@inglorious/store](https://www.npmjs.com/package/@inglorious/store)**.
|
|
7
|
+
|
|
8
|
+
Connect your React app to Inglorious Store with a familiar API. Built on `react-redux` for rock-solid performance and compatibility.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Drop-in Integration**: Works just like `react-redux` with enhanced features for Inglorious Store
|
|
15
|
+
- **Custom `useNotify` Hook**: Dispatch events with a clean, ergonomic API
|
|
16
|
+
- **Convenience `useEntity` Hook**: Select a single entity by its ID with a simple, optimized hook.
|
|
17
|
+
- **Battle-tested**: Built on `react-redux` for proven performance and stability
|
|
18
|
+
- **TypeScript Support**: Optional type safety for those who want it
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @inglorious/store @inglorious/react-store react react-dom
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### 1. Create Your Store
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
// store.js
|
|
36
|
+
import { createStore } from "@inglorious/store"
|
|
37
|
+
|
|
38
|
+
const types = {
|
|
39
|
+
counter: {
|
|
40
|
+
increment(counter) {
|
|
41
|
+
counter.value++
|
|
42
|
+
},
|
|
43
|
+
decrement(counter) {
|
|
44
|
+
counter.value--
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const entities = {
|
|
50
|
+
myCounter: { type: "counter", value: 0 },
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const store = createStore({ types, entities })
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. Set Up React Bindings
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
// react-store.js
|
|
60
|
+
import { createReactStore } from "@inglorious/react-store"
|
|
61
|
+
import { store } from "./store"
|
|
62
|
+
|
|
63
|
+
export const { Provider, useSelector, useNotify, useEntity } =
|
|
64
|
+
createReactStore(store)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Wrap Your App
|
|
68
|
+
|
|
69
|
+
```jsx
|
|
70
|
+
// App.jsx
|
|
71
|
+
import { Provider } from "./react-store"
|
|
72
|
+
|
|
73
|
+
function App() {
|
|
74
|
+
return (
|
|
75
|
+
<Provider>
|
|
76
|
+
<Counter />
|
|
77
|
+
</Provider>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 4. Use in Components
|
|
83
|
+
|
|
84
|
+
```jsx
|
|
85
|
+
// Counter.jsx
|
|
86
|
+
import { useNotify, useEntity } from "./react-store"
|
|
87
|
+
|
|
88
|
+
function Counter() {
|
|
89
|
+
const notify = useNotify()
|
|
90
|
+
const { value } = useEntity("myCounter")
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div>
|
|
94
|
+
<h1>Count: {value}</h1>
|
|
95
|
+
<button onClick={() => notify("increment")}>+</button>
|
|
96
|
+
<button onClick={() => notify("decrement")}>-</button>
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## API Reference
|
|
105
|
+
|
|
106
|
+
### `createReactStore(store)`
|
|
107
|
+
|
|
108
|
+
Creates React bindings for an Inglorious Store.
|
|
109
|
+
|
|
110
|
+
**Parameters:**
|
|
111
|
+
|
|
112
|
+
- `store` (required): An Inglorious Store instance
|
|
113
|
+
|
|
114
|
+
**Returns:**
|
|
115
|
+
|
|
116
|
+
- `Provider`: React context provider component (pre-configured with your store)
|
|
117
|
+
- `useSelector`: Hook to select state slices
|
|
118
|
+
- `useNotify`: Hook to dispatch events
|
|
119
|
+
- `useEntity`: Hook to select a single entity by ID
|
|
120
|
+
|
|
121
|
+
**Examples:**
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
const { Provider, useSelector, useNotify, useEntity } = createReactStore(store)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### `useNotify()`
|
|
128
|
+
|
|
129
|
+
Hook that returns a function to dispatch events.
|
|
130
|
+
|
|
131
|
+
**Returns:**
|
|
132
|
+
|
|
133
|
+
- `notify(type, payload?)`: Function to dispatch events
|
|
134
|
+
|
|
135
|
+
**Usage:**
|
|
136
|
+
|
|
137
|
+
```jsx
|
|
138
|
+
function TodoItem({ id }) {
|
|
139
|
+
const notify = useNotify()
|
|
140
|
+
|
|
141
|
+
// Simple event
|
|
142
|
+
const handleToggle = () => notify("toggleTodo", id)
|
|
143
|
+
|
|
144
|
+
// Event with complex payload
|
|
145
|
+
const handleRename = (text) => notify("renameTodo", { id, text })
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div>
|
|
149
|
+
<button onClick={handleToggle}>Toggle</button>
|
|
150
|
+
<input onChange={(e) => handleRename(e.target.value)} />
|
|
151
|
+
</div>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `useSelector(selector, equalityFn?)`
|
|
157
|
+
|
|
158
|
+
Hook to select and subscribe to state slices. Works exactly like `react-redux`'s `useSelector`.
|
|
159
|
+
|
|
160
|
+
**Parameters:**
|
|
161
|
+
|
|
162
|
+
- `selector`: Function that receives state and returns a slice
|
|
163
|
+
- `equalityFn` (optional): Custom equality function for optimization
|
|
164
|
+
|
|
165
|
+
**Usage:**
|
|
166
|
+
|
|
167
|
+
```jsx
|
|
168
|
+
function TodoList() {
|
|
169
|
+
// Select a specific entity
|
|
170
|
+
const task = useSelector((state) => state.task1)
|
|
171
|
+
|
|
172
|
+
// Select derived data
|
|
173
|
+
const completedCount = useSelector(
|
|
174
|
+
(state) => Object.values(state).filter((task) => task.completed).length,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
// With custom equality
|
|
178
|
+
const tasks = useSelector(
|
|
179
|
+
(state) => state.tasks,
|
|
180
|
+
(prev, next) => prev === next, // Shallow equality
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
return <div>...</div>
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### `useEntity(id)`
|
|
188
|
+
|
|
189
|
+
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.
|
|
190
|
+
|
|
191
|
+
**Parameters:**
|
|
192
|
+
|
|
193
|
+
- `id` (required): The ID of the entity to select.
|
|
194
|
+
|
|
195
|
+
**Usage:**
|
|
196
|
+
|
|
197
|
+
```jsx
|
|
198
|
+
function UserProfile() {
|
|
199
|
+
const user = useEntity("user-123")
|
|
200
|
+
if (!user) {
|
|
201
|
+
return "User not found."
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return user.name
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Redux DevTools Integration
|
|
211
|
+
|
|
212
|
+
Redux DevTools work automatically! Install the browser extension to inspect state snapshots, event history, and use time-travel debugging.
|
|
213
|
+
|
|
214
|
+
The underlying `@inglorious/store` allows for configuration, such as skipping certain events from being logged. See the store documentation for more details.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Complete Example: Todo App
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
// store.js
|
|
222
|
+
import { createStore } from "@inglorious/store"
|
|
223
|
+
|
|
224
|
+
const types = {
|
|
225
|
+
form: {
|
|
226
|
+
inputChange(entity, value) {
|
|
227
|
+
entity.value = value
|
|
228
|
+
},
|
|
229
|
+
formSubmit(entity) {
|
|
230
|
+
entity.value = ""
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
list: {
|
|
235
|
+
formSubmit(entity, value) {
|
|
236
|
+
entity.tasks.push({
|
|
237
|
+
id: Date.now(),
|
|
238
|
+
text: value,
|
|
239
|
+
completed: false,
|
|
240
|
+
})
|
|
241
|
+
},
|
|
242
|
+
toggleClick(entity, id) {
|
|
243
|
+
const task = entity.tasks.find((t) => t.id === id)
|
|
244
|
+
if (task) task.completed = !task.completed
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const entities = {
|
|
250
|
+
form: { type: "form", value: "" },
|
|
251
|
+
list: { type: "list", tasks: [] },
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export const store = createStore({ types, entities })
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
// react-store.js
|
|
259
|
+
import { createReactStore } from "@inglorious/react-store"
|
|
260
|
+
import { store } from "./store"
|
|
261
|
+
|
|
262
|
+
export const { Provider, useSelector, useNotify } = createReactStore(store)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```jsx
|
|
266
|
+
// App.jsx
|
|
267
|
+
import { Provider } from "./react-store"
|
|
268
|
+
import TodoApp from "./TodoApp"
|
|
269
|
+
|
|
270
|
+
export default function App() {
|
|
271
|
+
return (
|
|
272
|
+
<Provider>
|
|
273
|
+
<TodoApp />
|
|
274
|
+
</Provider>
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
```jsx
|
|
280
|
+
// TodoApp.jsx
|
|
281
|
+
import { useNotify, useSelector } from "./react-store"
|
|
282
|
+
|
|
283
|
+
export default function TodoApp() {
|
|
284
|
+
const notify = useNotify()
|
|
285
|
+
|
|
286
|
+
const formValue = useSelector((state) => state.form.value)
|
|
287
|
+
const tasks = useSelector((state) => state.list.tasks)
|
|
288
|
+
|
|
289
|
+
const handleSubmit = (e) => {
|
|
290
|
+
e.preventDefault()
|
|
291
|
+
notify("formSubmit", formValue)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return (
|
|
295
|
+
<div>
|
|
296
|
+
<form onSubmit={handleSubmit}>
|
|
297
|
+
<input
|
|
298
|
+
value={formValue}
|
|
299
|
+
onChange={(e) => notify("inputChange", e.target.value)}
|
|
300
|
+
placeholder="Add a task"
|
|
301
|
+
/>
|
|
302
|
+
<button type="submit">Add</button>
|
|
303
|
+
</form>
|
|
304
|
+
|
|
305
|
+
<ul>
|
|
306
|
+
{tasks.map((task) => (
|
|
307
|
+
<li key={task.id}>
|
|
308
|
+
<input
|
|
309
|
+
type="checkbox"
|
|
310
|
+
checked={task.completed}
|
|
311
|
+
onChange={() => notify("toggleClick", task.id)}
|
|
312
|
+
/>
|
|
313
|
+
<span
|
|
314
|
+
style={{
|
|
315
|
+
textDecoration: task.completed ? "line-through" : "none",
|
|
316
|
+
}}
|
|
317
|
+
>
|
|
318
|
+
{task.text}
|
|
319
|
+
</span>
|
|
320
|
+
</li>
|
|
321
|
+
))}
|
|
322
|
+
</ul>
|
|
323
|
+
</div>
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## TypeScript Support
|
|
331
|
+
|
|
332
|
+
Full TypeScript support is available! The library includes complete type definitions.
|
|
333
|
+
|
|
334
|
+
**Quick example:**
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
// Define your types
|
|
338
|
+
import { BaseEntity } from "@inglorious/store"
|
|
339
|
+
|
|
340
|
+
interface CounterEntity extends BaseEntity {
|
|
341
|
+
type: "counter"
|
|
342
|
+
value: number
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
interface AppState {
|
|
346
|
+
myCounter: CounterEntity
|
|
347
|
+
[id: string]: CounterEntity
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Create typed store
|
|
351
|
+
const store = createStore<CounterEntity, AppState>({ types, entities })
|
|
352
|
+
|
|
353
|
+
// Everything else works the same!
|
|
354
|
+
const { Provider, useSelector, useNotify } = createReactStore(store)
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
For complete TypeScript examples, see the [@inglorious/store TypeScript documentation](https://www.npmjs.com/package/@inglorious/store#-type-safety).
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## FAQ
|
|
362
|
+
|
|
363
|
+
**Q: Can I use this with existing react-redux code?**
|
|
364
|
+
A: Yes! The `Provider` and `useSelector` are compatible. You can gradually migrate to `useNotify`.
|
|
365
|
+
|
|
366
|
+
**Q: Does this work with React Native?**
|
|
367
|
+
A: Yes! It works anywhere `react-redux` works.
|
|
368
|
+
|
|
369
|
+
**Q: Can I use Redux middleware?**
|
|
370
|
+
A: Use Inglorious Store middleware instead. See [@inglorious/store docs](https://www.npmjs.com/package/@inglorious/store).
|
|
371
|
+
|
|
372
|
+
**Q: Do I need TypeScript?**
|
|
373
|
+
A: Not at all! The library works great with plain JavaScript. TypeScript support is completely optional.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Comparison to Plain react-redux
|
|
378
|
+
|
|
379
|
+
**What's the same:**
|
|
380
|
+
|
|
381
|
+
- `<Provider>` and `useSelector` work identically
|
|
382
|
+
- Full Redux DevTools support
|
|
383
|
+
- Same performance characteristics
|
|
384
|
+
|
|
385
|
+
**What's different:**
|
|
386
|
+
|
|
387
|
+
- ✅ `useNotify` hook for dispatching events instead of `useDispatch`
|
|
388
|
+
- ✅ `useEntity` hook for easily selecting an entity by ID
|
|
389
|
+
- ✅ Cleaner API for event-based state management
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## License
|
|
394
|
+
|
|
395
|
+
**MIT License - Free and open source**
|
|
396
|
+
|
|
397
|
+
Created by [Matteo Antony Mistretta](https://github.com/IngloriousCoderz)
|
|
398
|
+
|
|
399
|
+
You're free to use, modify, and distribute this software. See [LICENSE](./LICENSE) for details.
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Contributing
|
|
404
|
+
|
|
405
|
+
Contributions welcome! Please read our [Contributing Guidelines](../../CONTRIBUTING.md) first.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/react-store",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.1",
|
|
4
4
|
"description": "Official React bindings for @inglorious/store. Provides hooks and a Provider to connect your React components to the store.",
|
|
5
5
|
"author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"react": "^19.2.0",
|
|
45
|
-
"@inglorious/store": "
|
|
45
|
+
"@inglorious/store": "9.0.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"prettier": "^3.6.2",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"node": ">= 22"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
|
-
"format": "prettier --write
|
|
57
|
-
"lint": "eslint .
|
|
56
|
+
"format": "prettier --write .",
|
|
57
|
+
"lint": "eslint ."
|
|
58
58
|
}
|
|
59
59
|
}
|