@inglorious/react-store 1.0.0
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/LICENSE +9 -0
- package/README.md +445 -0
- package/package.json +57 -0
- package/src/index.jsx +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright © 2025 Inglorious Coderz Srl.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
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://github.com/IngloriousCoderz/inglorious-engine/tree/main/packages/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
|
+
- **Flexible Update Modes**:
|
|
17
|
+
- **Eager mode** - Updates process immediately (responsive UIs)
|
|
18
|
+
- **Batched mode** - Updates process on a timer (performance optimization)
|
|
19
|
+
- **Redux DevTools Support**: Full integration with Redux DevTools for debugging
|
|
20
|
+
- **Battle-tested**: Built on `react-redux` for proven performance and stability
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @inglorious/store @inglorious/react-store react react-dom
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### 1. Create Your Store
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
// store.js
|
|
38
|
+
import { createStore } from "@inglorious/store"
|
|
39
|
+
|
|
40
|
+
const types = {
|
|
41
|
+
counter: {
|
|
42
|
+
increment(counter) {
|
|
43
|
+
counter.value++
|
|
44
|
+
},
|
|
45
|
+
decrement(counter) {
|
|
46
|
+
counter.value--
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const entities = {
|
|
52
|
+
myCounter: { type: "counter", value: 0 },
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const store = createStore({ types, entities })
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. Set Up React Bindings
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
// react-store.js
|
|
62
|
+
import { createReactStore } from "@inglorious/react-store"
|
|
63
|
+
import { store } from "./store"
|
|
64
|
+
|
|
65
|
+
// Eager mode (default) - updates process immediately
|
|
66
|
+
export const { Provider, useSelector, useNotify } = createReactStore(store)
|
|
67
|
+
|
|
68
|
+
// Or batched mode - updates process at 30 FPS
|
|
69
|
+
// export const { Provider, useSelector, useNotify } = createReactStore(store, {
|
|
70
|
+
// mode: "batched",
|
|
71
|
+
// fps: 30
|
|
72
|
+
// })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3. Wrap Your App
|
|
76
|
+
|
|
77
|
+
```jsx
|
|
78
|
+
// App.jsx
|
|
79
|
+
import { Provider } from "./react-store"
|
|
80
|
+
|
|
81
|
+
function App() {
|
|
82
|
+
return (
|
|
83
|
+
<Provider>
|
|
84
|
+
<Counter />
|
|
85
|
+
</Provider>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 4. Use in Components
|
|
91
|
+
|
|
92
|
+
```jsx
|
|
93
|
+
// Counter.jsx
|
|
94
|
+
import { useNotify, useSelector } from "./react-store"
|
|
95
|
+
|
|
96
|
+
function Counter() {
|
|
97
|
+
const notify = useNotify()
|
|
98
|
+
const value = useSelector((state) => state.myCounter.value)
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<div>
|
|
102
|
+
<h1>Count: {value}</h1>
|
|
103
|
+
<button onClick={() => notify("increment")}>+</button>
|
|
104
|
+
<button onClick={() => notify("decrement")}>-</button>
|
|
105
|
+
</div>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## API Reference
|
|
113
|
+
|
|
114
|
+
### `createReactStore(store, config?)`
|
|
115
|
+
|
|
116
|
+
Creates React bindings for an Inglorious Store.
|
|
117
|
+
|
|
118
|
+
**Parameters:**
|
|
119
|
+
|
|
120
|
+
- `store` (required): An Inglorious Store instance
|
|
121
|
+
- `config` (optional): Configuration object
|
|
122
|
+
- `mode`: `"eager"` (default) or `"batched"`
|
|
123
|
+
- `fps`: Frame rate for batched mode (default: 30)
|
|
124
|
+
- `skippedEvents`: Array of event types to exclude from DevTools logging
|
|
125
|
+
|
|
126
|
+
**Returns:**
|
|
127
|
+
|
|
128
|
+
- `Provider`: React context provider component (pre-configured with your store)
|
|
129
|
+
- `useSelector`: Hook to select state slices
|
|
130
|
+
- `useNotify`: Hook to dispatch events
|
|
131
|
+
|
|
132
|
+
**Examples:**
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
// Eager mode (immediate updates)
|
|
136
|
+
const { Provider, useSelector, useNotify } = createReactStore(store)
|
|
137
|
+
|
|
138
|
+
// Batched mode (30 FPS)
|
|
139
|
+
const { Provider, useSelector, useNotify } = createReactStore(store, {
|
|
140
|
+
mode: "batched",
|
|
141
|
+
fps: 30,
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// Custom FPS for animations
|
|
145
|
+
const { Provider, useSelector, useNotify } = createReactStore(store, {
|
|
146
|
+
mode: "batched",
|
|
147
|
+
fps: 60,
|
|
148
|
+
skippedEvents: ["update", "mousemove"], // Don't log these in DevTools
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### `useNotify()`
|
|
153
|
+
|
|
154
|
+
Hook that returns a function to dispatch events.
|
|
155
|
+
|
|
156
|
+
**Returns:**
|
|
157
|
+
|
|
158
|
+
- `notify(type, payload?)`: Function to dispatch events
|
|
159
|
+
|
|
160
|
+
**Usage:**
|
|
161
|
+
|
|
162
|
+
```jsx
|
|
163
|
+
function TodoItem({ id }) {
|
|
164
|
+
const notify = useNotify()
|
|
165
|
+
|
|
166
|
+
// Simple event
|
|
167
|
+
const handleToggle = () => notify("toggleTodo", id)
|
|
168
|
+
|
|
169
|
+
// Event with complex payload
|
|
170
|
+
const handleRename = (text) => notify("renameTodo", { id, text })
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<div>
|
|
174
|
+
<button onClick={handleToggle}>Toggle</button>
|
|
175
|
+
<input onChange={(e) => handleRename(e.target.value)} />
|
|
176
|
+
</div>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### `useSelector(selector, equalityFn?)`
|
|
182
|
+
|
|
183
|
+
Hook to select and subscribe to state slices. Works exactly like `react-redux`'s `useSelector`.
|
|
184
|
+
|
|
185
|
+
**Parameters:**
|
|
186
|
+
|
|
187
|
+
- `selector`: Function that receives state and returns a slice
|
|
188
|
+
- `equalityFn` (optional): Custom equality function for optimization
|
|
189
|
+
|
|
190
|
+
**Usage:**
|
|
191
|
+
|
|
192
|
+
```jsx
|
|
193
|
+
function TodoList() {
|
|
194
|
+
// Select a specific entity
|
|
195
|
+
const task = useSelector((state) => state.task1)
|
|
196
|
+
|
|
197
|
+
// Select derived data
|
|
198
|
+
const completedCount = useSelector(
|
|
199
|
+
(state) => Object.values(state).filter((task) => task.completed).length,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
// With custom equality
|
|
203
|
+
const tasks = useSelector(
|
|
204
|
+
(state) => state.tasks,
|
|
205
|
+
(prev, next) => prev === next, // Shallow equality
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return <div>...</div>
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Update Modes
|
|
215
|
+
|
|
216
|
+
### Eager Mode (Default)
|
|
217
|
+
|
|
218
|
+
Best for most apps. Updates process immediately when you call `notify()`.
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
const { Provider, useSelector, useNotify } = createReactStore(store)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**When to use:**
|
|
225
|
+
|
|
226
|
+
- ✅ Standard apps (forms, CRUD, dashboards)
|
|
227
|
+
- ✅ You want instant feedback on user actions
|
|
228
|
+
- ✅ You're not processing many events per second
|
|
229
|
+
|
|
230
|
+
### Batched Mode
|
|
231
|
+
|
|
232
|
+
Best for performance-critical apps. Updates process on a fixed timer (FPS).
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
const { Provider, useSelector, useNotify } = createReactStore(store, {
|
|
236
|
+
mode: "batched",
|
|
237
|
+
fps: 30, // Process events 30 times per second
|
|
238
|
+
})
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**When to use:**
|
|
242
|
+
|
|
243
|
+
- ✅ Games or animations
|
|
244
|
+
- ✅ High-frequency events (mouse tracking, real-time data)
|
|
245
|
+
- ✅ You want to batch multiple events into one React render
|
|
246
|
+
- ✅ Performance optimization is critical
|
|
247
|
+
|
|
248
|
+
**FPS Guidelines:**
|
|
249
|
+
|
|
250
|
+
- **60 FPS** - Smooth animations, games
|
|
251
|
+
- **30 FPS (default)** - Good balance for most real-time apps
|
|
252
|
+
- **15-20 FPS** - Live dashboards, lower CPU usage
|
|
253
|
+
- **5-10 FPS** - Background updates, status polling
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Redux DevTools Integration
|
|
258
|
+
|
|
259
|
+
Redux DevTools work automatically! Install the [browser extension](https://github.com/reduxjs/redux-devtools) to inspect:
|
|
260
|
+
|
|
261
|
+
- State snapshots
|
|
262
|
+
- Event history
|
|
263
|
+
- Time-travel debugging
|
|
264
|
+
|
|
265
|
+
**In batched mode**, events are automatically grouped by frame for cleaner DevTools logs.
|
|
266
|
+
|
|
267
|
+
```javascript
|
|
268
|
+
// Skip noisy events from DevTools
|
|
269
|
+
const { Provider, useSelector, useNotify } = createReactStore(store, {
|
|
270
|
+
mode: "batched",
|
|
271
|
+
fps: 30,
|
|
272
|
+
skippedEvents: ["mousemove", "update"], // Don't log these
|
|
273
|
+
})
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Complete Example: Todo App
|
|
279
|
+
|
|
280
|
+
```javascript
|
|
281
|
+
// store.js
|
|
282
|
+
import { createStore } from "@inglorious/store"
|
|
283
|
+
|
|
284
|
+
const types = {
|
|
285
|
+
form: {
|
|
286
|
+
inputChange(entity, value) {
|
|
287
|
+
entity.value = value
|
|
288
|
+
},
|
|
289
|
+
formSubmit(entity) {
|
|
290
|
+
entity.value = ""
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
list: {
|
|
295
|
+
formSubmit(entity, value) {
|
|
296
|
+
entity.tasks.push({
|
|
297
|
+
id: Date.now(),
|
|
298
|
+
text: value,
|
|
299
|
+
completed: false,
|
|
300
|
+
})
|
|
301
|
+
},
|
|
302
|
+
toggleClick(entity, id) {
|
|
303
|
+
const task = entity.tasks.find((t) => t.id === id)
|
|
304
|
+
if (task) task.completed = !task.completed
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const entities = {
|
|
310
|
+
form: { type: "form", value: "" },
|
|
311
|
+
list: { type: "list", tasks: [] },
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export const store = createStore({ types, entities })
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
```javascript
|
|
318
|
+
// react-store.js
|
|
319
|
+
import { createReactStore } from "@inglorious/react-store"
|
|
320
|
+
import { store } from "./store"
|
|
321
|
+
|
|
322
|
+
export const { Provider, useSelector, useNotify } = createReactStore(store)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
```jsx
|
|
326
|
+
// App.jsx
|
|
327
|
+
import { Provider } from "./react-store"
|
|
328
|
+
import TodoApp from "./TodoApp"
|
|
329
|
+
|
|
330
|
+
export default function App() {
|
|
331
|
+
return (
|
|
332
|
+
<Provider>
|
|
333
|
+
<TodoApp />
|
|
334
|
+
</Provider>
|
|
335
|
+
)
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
```jsx
|
|
340
|
+
// TodoApp.jsx
|
|
341
|
+
import { useNotify, useSelector } from "./react-store"
|
|
342
|
+
|
|
343
|
+
export default function TodoApp() {
|
|
344
|
+
const notify = useNotify()
|
|
345
|
+
|
|
346
|
+
const formValue = useSelector((state) => state.form.value)
|
|
347
|
+
const tasks = useSelector((state) => state.list.tasks)
|
|
348
|
+
|
|
349
|
+
const handleSubmit = (e) => {
|
|
350
|
+
e.preventDefault()
|
|
351
|
+
notify("formSubmit", formValue)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return (
|
|
355
|
+
<div>
|
|
356
|
+
<form onSubmit={handleSubmit}>
|
|
357
|
+
<input
|
|
358
|
+
value={formValue}
|
|
359
|
+
onChange={(e) => notify("inputChange", e.target.value)}
|
|
360
|
+
placeholder="Add a task"
|
|
361
|
+
/>
|
|
362
|
+
<button type="submit">Add</button>
|
|
363
|
+
</form>
|
|
364
|
+
|
|
365
|
+
<ul>
|
|
366
|
+
{tasks.map((task) => (
|
|
367
|
+
<li key={task.id}>
|
|
368
|
+
<input
|
|
369
|
+
type="checkbox"
|
|
370
|
+
checked={task.completed}
|
|
371
|
+
onChange={() => notify("toggleClick", task.id)}
|
|
372
|
+
/>
|
|
373
|
+
<span
|
|
374
|
+
style={{
|
|
375
|
+
textDecoration: task.completed ? "line-through" : "none",
|
|
376
|
+
}}
|
|
377
|
+
>
|
|
378
|
+
{task.text}
|
|
379
|
+
</span>
|
|
380
|
+
</li>
|
|
381
|
+
))}
|
|
382
|
+
</ul>
|
|
383
|
+
</div>
|
|
384
|
+
)
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## TypeScript Support
|
|
391
|
+
|
|
392
|
+
Full TypeScript support coming soon! For now, you can add type assertions:
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
import type { RootState } from "./store"
|
|
396
|
+
|
|
397
|
+
const value = useSelector((state: RootState) => state.myCounter.value)
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Comparison to Plain react-redux
|
|
403
|
+
|
|
404
|
+
**What's the same:**
|
|
405
|
+
|
|
406
|
+
- `<Provider>` and `useSelector` work identically
|
|
407
|
+
- Full Redux DevTools support
|
|
408
|
+
- Same performance characteristics
|
|
409
|
+
|
|
410
|
+
**What's different:**
|
|
411
|
+
|
|
412
|
+
- ✅ Custom `useNotify` hook instead of `useDispatch`
|
|
413
|
+
- ✅ Batched mode option for performance
|
|
414
|
+
- ✅ Automatic `store.update()` handling
|
|
415
|
+
- ✅ Cleaner API for event-based state management
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## FAQ
|
|
420
|
+
|
|
421
|
+
**Q: Can I use this with existing react-redux code?**
|
|
422
|
+
A: Yes! The `Provider` and `useSelector` are compatible. You can gradually migrate to `useNotify`.
|
|
423
|
+
|
|
424
|
+
**Q: Should I use eager or batched mode?**
|
|
425
|
+
A: Start with eager (default). Switch to batched if you notice performance issues or are building a game/real-time app.
|
|
426
|
+
|
|
427
|
+
**Q: Does this work with React Native?**
|
|
428
|
+
A: Yes! It works anywhere `react-redux` works.
|
|
429
|
+
|
|
430
|
+
**Q: Can I use Redux middleware?**
|
|
431
|
+
A: Use Inglorious Store middleware instead. See [@inglorious/store docs](https://github.com/IngloriousCoderz/inglorious-engine/tree/main/packages/store).
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## License
|
|
436
|
+
|
|
437
|
+
MIT © [Matteo Antony Mistretta](https://github.com/IngloriousCoderz)
|
|
438
|
+
|
|
439
|
+
This is free and open-source software. Use it however you want!
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Contributing
|
|
444
|
+
|
|
445
|
+
Contributions welcome! Please read our [Contributing Guidelines](../../CONTRIBUTING.md) first.
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@inglorious/react-store",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official React bindings for @inglorious/store. Provides hooks and a Provider to connect your React components to the store.",
|
|
5
|
+
"author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/IngloriousCoderz/inglorious-engine.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://inglorious-engine.vercel.app/",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/IngloriousCoderz/inglorious-engine/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"react",
|
|
17
|
+
"react-hooks",
|
|
18
|
+
"hooks",
|
|
19
|
+
"state",
|
|
20
|
+
"state-management",
|
|
21
|
+
"store",
|
|
22
|
+
"redux",
|
|
23
|
+
"react-redux",
|
|
24
|
+
"inglorious-store"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"import": "./src/index.jsx"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"src"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"react-redux": "^9.1.2"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": "^18.2.0",
|
|
43
|
+
"@inglorious/store": "5.2.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"prettier": "^3.6.2",
|
|
47
|
+
"vite": "^7.1.3",
|
|
48
|
+
"@inglorious/eslint-config": "1.0.1"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">= 22"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"format": "prettier --write '**/*.{js,jsx}'",
|
|
55
|
+
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/index.jsx
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { sendAction } from "@inglorious/store/client/dev-tools"
|
|
2
|
+
import { Provider as BaseProvider, useDispatch, useSelector } from "react-redux"
|
|
3
|
+
|
|
4
|
+
const DEFAULT_CONFIG = { mode: "eager" }
|
|
5
|
+
const ONE_SECOND = 1000
|
|
6
|
+
const DEFAULT_FPS = 30
|
|
7
|
+
|
|
8
|
+
export function createReactStore(store, config = DEFAULT_CONFIG) {
|
|
9
|
+
if (config.mode === "batched") {
|
|
10
|
+
loop(store, config)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return { Provider, useSelector, useNotify }
|
|
14
|
+
|
|
15
|
+
function Provider({ children }) {
|
|
16
|
+
return <BaseProvider store={store}>{children}</BaseProvider>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function useNotify() {
|
|
20
|
+
const dispatch = useDispatch()
|
|
21
|
+
|
|
22
|
+
return (type, payload) => {
|
|
23
|
+
const action = { type, payload }
|
|
24
|
+
dispatch(action)
|
|
25
|
+
|
|
26
|
+
if (config.mode === "eager") {
|
|
27
|
+
store.update()
|
|
28
|
+
sendAction(action, store.getState())
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function loop(store, config) {
|
|
35
|
+
const fps = config.fps ?? DEFAULT_FPS
|
|
36
|
+
|
|
37
|
+
setInterval(() => {
|
|
38
|
+
const processedEvents = store.update()
|
|
39
|
+
const skippedEvents = config.skippedEvents ?? []
|
|
40
|
+
|
|
41
|
+
const eventsToLog = processedEvents.filter(
|
|
42
|
+
({ type }) => !skippedEvents.includes(type),
|
|
43
|
+
)
|
|
44
|
+
if (eventsToLog.length) {
|
|
45
|
+
const action = {
|
|
46
|
+
type: eventsToLog.map(({ type }) => type).join("|"),
|
|
47
|
+
payload: eventsToLog,
|
|
48
|
+
}
|
|
49
|
+
sendAction(action, store.getState())
|
|
50
|
+
}
|
|
51
|
+
}, ONE_SECOND / fps)
|
|
52
|
+
}
|