reactish-state 0.10.2 → 0.10.3

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 ADDED
@@ -0,0 +1,542 @@
1
+ # Reactish-State
2
+
3
+ > Simple, decentralized state management for React.
4
+
5
+ ## Install
6
+
7
+ `npm install reactish-state` or `yarn add reactish-state`
8
+
9
+ ## Quick start
10
+
11
+ ### We begin by creating some states
12
+
13
+ ```js
14
+ import { state } from "reactish-state";
15
+
16
+ // `state` can hold anything: primitives, arrays, objects...
17
+ const countState = state(0);
18
+ const todos = state([
19
+ { task: "Shop groceries", completed: false },
20
+ { task: "Clean the house", completed: true }
21
+ ]);
22
+
23
+ // Update state
24
+ countState.set(10);
25
+ // Read from state
26
+ console.log(countState.get()); // Print 10
27
+ ```
28
+
29
+ ### A state can also have actions bound to it
30
+
31
+ ```js
32
+ const countState = state(0, (set, get) => ({
33
+ // Set a new state
34
+ reset: () => set(0),
35
+ // or using the functional update of `set`
36
+ increase: () => set((count) => count + 1),
37
+ // State can still be read using `get`
38
+ decrease: () => set(get() - 1)
39
+ }));
40
+
41
+ // Using the actions
42
+ countState.actions.increase();
43
+ ```
44
+
45
+ ### `selector` can create derived states
46
+
47
+ ```js
48
+ import { selector } from "reactish-state";
49
+
50
+ // Derive from another state
51
+ const doubleSelector = selector(countState, (count) => count * 2);
52
+
53
+ // Can also derive from both states and selectors
54
+ const tripleSelector = selector(
55
+ countState,
56
+ doubleSelector,
57
+ (count, double) => count + double
58
+ );
59
+ ```
60
+
61
+ A selector will re-compute only when any of the states it depends on have changed.
62
+
63
+ ### Use the state and selector in your React components
64
+
65
+ You can read state and selector for rendering with the `useSnapshot` hook, and write to state with `set` or actions. _Rule of thumb_: always read from `useSnapshot` in render function, otherwise use the `get` method of state or selector.
66
+
67
+ ```jsx
68
+ import { useSnapshot } from "reactish-state";
69
+
70
+ const Example = () => {
71
+ const count = useSnapshot(countState);
72
+ const triple = useSnapshot(tripleSelector);
73
+
74
+ return (
75
+ <h1>
76
+ {count} {triple}
77
+ {/* Update state using the actions bound to it */}
78
+ <button onClick={() => countState.actions.increase()}>Increase</button>
79
+ {/* Or update state using the `set` method directly */}
80
+ <button onClick={() => countState.set((i) => i - 1)}>Decrease</button>
81
+ <button onClick={() => countState.set(0)}>Reset</button>
82
+ </h1>
83
+ );
84
+ };
85
+ ```
86
+
87
+ The component will re-render when states or selectors have changed. No provider or context are needed!
88
+
89
+ **[Try a sandbox demo!](https://codesandbox.io/s/reactish-counter-3let0o)**
90
+
91
+ ## Why another state management library?
92
+
93
+ The state management solutions in the React ecosystem have popularized two state models:
94
+
95
+ - **Centralized**: a single store that combines entire app states together and slices of the store are connected to React components through selectors. Examples: react-redux, Zustand.
96
+
97
+ - **Decentralized**: consisting of many small states which can build up state dependency trees using a bottom-up approach. React components only need to connect with the states that they use. Examples: Recoil, Jotai.
98
+
99
+ This library adopts the decentralized state model, offering a _Recoil-like_ API, but with a much simpler and smaller implementation(similar to Zustand), which makes it the one of the smallest state management solutions with gzipped bundle size less than 1KB.
100
+
101
+ | | State model | Bundle size |
102
+ | --- | --- | --- |
103
+ | Reactish-State | decentralized | [![NPM](https://img.shields.io/bundlephobia/minzip/reactish-state)](https://bundlephobia.com/package/reactish-state) |
104
+ | Recoil | decentralized | [![NPM](https://img.shields.io/bundlephobia/minzip/recoil)](https://bundlephobia.com/package/recoil) |
105
+ | Jotai | decentralized | [![NPM](https://img.shields.io/bundlephobia/minzip/jotai)](https://bundlephobia.com/package/jotai) |
106
+ | React-Redux | centralized | [![NPM](https://img.shields.io/bundlephobia/minzip/react-redux)](https://bundlephobia.com/package/react-redux) |
107
+ | Zustand | centralized | [![NPM](https://img.shields.io/bundlephobia/minzip/zustand)](https://bundlephobia.com/package/zustand) |
108
+
109
+ ## Why decentralized state management?
110
+
111
+ Centralized state management usually combines the entire app states into a single store. To achieve render optimization, selectors are used to subscribe React components to slices of the store. Taking the classic [Redux todo example](https://redux.js.org/introduction/examples#todos), the store has the following shape:
112
+
113
+ ```js
114
+ {
115
+ visibilityFilter: "ALL", // ALL, ACTIVE, COMPLETED
116
+ todos: [{ task: "Shop groceries", completed: false } /* ...more items */]
117
+ }
118
+ ```
119
+
120
+ We have a `<Filter/>` component that connects to the store with a selector `(state) => state.visibilityFilter`.
121
+
122
+ When any action updates the `todos` slice, the selector in the `<Filter/>` component needs to re-run to determine if a re-rendering of the component is required. This is not optimal as `<Filter/>` component should not even be bothered when the todos are added/removed/updated.
123
+
124
+ In contrast, decentralized state management may approach the same problem with two separate states:
125
+
126
+ ```js
127
+ const visibilityFilter = state("ALL"); // ALL, ACTIVE, COMPLETED
128
+ const todos = state([
129
+ { task: "Shop groceries", completed: false }
130
+ /* ...more items */
131
+ ]);
132
+ ```
133
+
134
+ An update of `todos`, which is localized and isolated from other states, does not affect the components connected to `visibilityFilter` and vice versa.
135
+
136
+ The difference might sound insignificant, but imaging every single state update could cause every selector in every component in the entire app to run again, it suggests that decentralized state model scales better for large apps. In addition, some other benefits such as code-splitting are made easier by this state model.
137
+
138
+ ## Why this over Zustand?
139
+
140
+ - State updates localized and isolated from other irrelevant states.
141
+ - No potential naming conflicts among states/actions within the big store.
142
+ - No need to use a React Hook to extract actions from the store.
143
+ - Actions come from outside React and no need to add them into the `useCallback/useEffect` dep array.
144
+
145
+ ## ✨Highlights✨
146
+
147
+ - Decentralized state management
148
+ - Un-opinionated and easy-to-use API
149
+ - No need of wrapping app in Context or prop drilling
150
+ - React components re-render only on changes
151
+ - Compatible with React 18 concurrent rendering
152
+ - Selectors are memoized by default
153
+ - Feature extensible with middleware or plugins
154
+ - States persistable to browser storage
155
+ - Support Redux dev tools via middleware
156
+ - Less than 1KB: simple and small
157
+
158
+ # Recipes
159
+
160
+ ## States should be updated immutably
161
+
162
+ ```js
163
+ import { state } from "reactish-state";
164
+
165
+ const todosState = state([{ task: "Clean the house", completed: true }]);
166
+ todosState.set((todos) => [
167
+ ...todos,
168
+ { task: "Shop groceries", completed: false }
169
+ ]);
170
+ ```
171
+
172
+ You can use the `immer` package to reduce boilerplate code:
173
+
174
+ ```js
175
+ import produce from "immer";
176
+
177
+ todosState.set(
178
+ produce((todos) => {
179
+ todos.push({ task: "Shop groceries", completed: false });
180
+ })
181
+ );
182
+ ```
183
+
184
+ Or, simply use the [immer middleware](#immer-middleware).
185
+
186
+ ## Selectors are memoized
187
+
188
+ Selector has an API that is similar to the [reselect](https://github.com/reduxjs/reselect#readme) package. You pass in one or more "input" states or selectors, and an "output" selector function that receives the extracted values and should return a derived value. The return value is memoized so that it won't cause React components to re-render even if non-primitive value is returned.
189
+
190
+ ```js
191
+ import { selector } from "reactish-state";
192
+
193
+ // Return a number
194
+ const totalNumSelector = selector(todosState, (todos) => todos.length);
195
+
196
+ // Return a new array
197
+ const completedTodosSelector = selector(todosState, (todos) =>
198
+ todos.filter((todo) => todo.completed)
199
+ );
200
+
201
+ // Return an object
202
+ const todoStats = selector(
203
+ totalNumSelector,
204
+ completedTodosSelector,
205
+ (totalNum, completedTodos) => ({
206
+ completedNum: completedTodos.length,
207
+ percentCompleted: (completedTodos.length / totalNum) * 100
208
+ })
209
+ );
210
+ ```
211
+
212
+ ## Async state updates
213
+
214
+ Just call `set` when you're ready:
215
+
216
+ ```js
217
+ const todosState = state([]);
218
+
219
+ async function fetchTodos(url) {
220
+ const response = await fetch(url);
221
+ todosState.set(await response.json());
222
+ }
223
+ ```
224
+
225
+ You can also create async actions bound to a state:
226
+
227
+ ```js
228
+ const todosState = state([], (set) => ({
229
+ fetch: async (url) => {
230
+ const response = await fetch(url);
231
+ set(await response.json());
232
+ }
233
+ }));
234
+ ```
235
+
236
+ ## Accessing other state or selector inside actions
237
+
238
+ You might not need it, but nothing stops you from reading or writing to other state inside an action.
239
+
240
+ ```js
241
+ const inputState = state("New item");
242
+ const todosState = state(
243
+ [{ task: "Shop groceries", completed: false }],
244
+ (set) => ({
245
+ add: () => {
246
+ set((todos) => [...todos, { task: inputState.get(), completed: false }]);
247
+ inputState.set(""); // Reset input after adding a todo
248
+ }
249
+ })
250
+ );
251
+ ```
252
+
253
+ ## Interacting with state or selector outside React
254
+
255
+ ```js
256
+ const countState = state(0);
257
+ const tripleSelector = selector(countState, (count) => count * 3);
258
+
259
+ // Get a non-reactish fresh value
260
+ const count = countState.get();
261
+ const triple = tripleSelector.get();
262
+
263
+ // Listen to updates
264
+ const unsub1 = countState.subscribe(() => console.log(countState.get()));
265
+ const unsub2 = tripleSelector.subscribe(() =>
266
+ console.log(tripleSelector.get())
267
+ );
268
+
269
+ // Update `countState`, will trigger both listeners
270
+ countState.set(10);
271
+
272
+ // Unsubscribe listeners
273
+ unsub1();
274
+ unsub2();
275
+ ```
276
+
277
+ The only difference between state and selector is that selectors are read-only which don't have a `set` method.
278
+
279
+ ## Destructuring actions for easier reference
280
+
281
+ The `set` or actions of a state don't rely on `this` to work, thus you are free to destructure them for easier reference.
282
+
283
+ _TIP_: destructure the actions outside React components so that you don't need to add them into the `useCallback/useEffect` dependency array.
284
+
285
+ ```jsx
286
+ import { state, useSnapshot } from "reactish-state";
287
+
288
+ const countState = state(0, (set) => ({
289
+ increase: () => set((count) => count + 1),
290
+ reset: () => set(0)
291
+ }));
292
+ const { increase, reset } = countState.actions;
293
+
294
+ const Example = () => {
295
+ const count = useSnapshot(countState);
296
+ return (
297
+ <h1>
298
+ {count}
299
+ <button onClick={() => increase()}>Increase</button>
300
+ <button onClick={() => reset()}>Reset</button>
301
+ </h1>
302
+ );
303
+ };
304
+ ```
305
+
306
+ ## Still perfer Redux-like reducers?
307
+
308
+ ```js
309
+ const reducer = (state, { type, by = 1 }) => {
310
+ switch (type) {
311
+ case "INCREASE":
312
+ return state + by;
313
+ case "DECREASE":
314
+ return state - by;
315
+ }
316
+ };
317
+
318
+ const countState = state(0, (set) => ({
319
+ dispatch: (action) => set((state) => reducer(state, action), action)
320
+ }));
321
+
322
+ const { dispatch } = countState.actions;
323
+ dispatch({ type: "INCREASE", by: 10 });
324
+ dispatch({ type: "DECREASE", by: 7 });
325
+ console.log(countState.get()); // Print 3
326
+ ```
327
+
328
+ ## Middleware
329
+
330
+ You can enhance the functionalities of state with middleware. Instead of using the `state` export, you use the `createState` export from the library. Middleware is a function which receives `set`, `get` and `subscribe` and should return a new set function.
331
+
332
+ ```js
333
+ import { createState } from "reactish-state";
334
+
335
+ const state = createState({
336
+ middleware:
337
+ ({ set, get }) =>
338
+ (...args) => {
339
+ set(...args);
340
+ // Log state every time after calling `set`
341
+ console.log("New state", get());
342
+ }
343
+ });
344
+
345
+ // Now the `state` function has wired up a middleware
346
+ const countState = state(0, (set) => ({
347
+ increase: () => set((count) => count + 1)
348
+ }));
349
+
350
+ countState.set(99); // Print "New state 99"
351
+ countState.actions.increase(); // Print "New state 100"
352
+
353
+ // The same `state` function can be reused,
354
+ // thus you don't need to set up the middleware again
355
+ const filterState = state("ALL");
356
+ filterState.set("COMPLETED"); // Print "New state 'COMPLETED'"
357
+ ```
358
+
359
+ ## Persist middleware
360
+
361
+ You can save state in browser storage with the `persist` middleware.
362
+
363
+ ```js
364
+ import { createState } from "reactish-state";
365
+ import { persist } from "reactish-state/middleware";
366
+
367
+ // Create the persist middleware,
368
+ // you can optionally provide a `prefix` prepended to the keys in storage
369
+ const persistMiddleware = persist({ prefix: "myApp-" });
370
+ const state = createState({ middleware: persistMiddleware });
371
+
372
+ const countState = state(
373
+ 0,
374
+ (set) => ({
375
+ increase: () => set((count) => count + 1)
376
+ }),
377
+ { key: "count" } // In the third parameter, give each state a unique key
378
+ );
379
+ const filterState = state("ALL", null, { key: "filter" });
380
+
381
+ // Hydrate all the states created with this middleware from storage
382
+ useEffect(() => {
383
+ // Call `hydrate` in an useEffect to avoid client-side mismatch
384
+ // if React components are also server-rendered
385
+ persistMiddleware.hydrate();
386
+ }, []);
387
+ // You can add the `useEffect` once into your root component
388
+ ```
389
+
390
+ By default `localStorage` is used to persist states. You can change it to `sessionStorage` or other implementations using the `getStorage` option.
391
+
392
+ ```js
393
+ const persistMiddleware = persist({ getStorage: () => sessionStorage });
394
+ ```
395
+
396
+ ## Immer middleware
397
+
398
+ You can mutably update state with the `immer` middleware.
399
+
400
+ ```js
401
+ import { createState } from "reactish-state";
402
+ import { immer } from "reactish-state/middleware/immer";
403
+
404
+ const state = createState({ middleware: immer });
405
+
406
+ let todoId = 1;
407
+ const todos = state([], (set) => ({
408
+ add: (task) =>
409
+ set((todos) => {
410
+ todos.push({ id: todoId++, task, completed: false });
411
+ // Need to return the draft state for correct typing in TypeScript code
412
+ // return todos;
413
+ }),
414
+
415
+ toggle: (id) =>
416
+ set((todos) => {
417
+ const todo = todos.find((todo) => todo.id === id);
418
+ if (todo) todo.completed = !todo.completed;
419
+ })
420
+ }));
421
+
422
+ // Using the actions
423
+ todos.actions.add("Shop groceries");
424
+ todos.actions.toggle(1);
425
+ ```
426
+
427
+ ## Redux devtools middleware
428
+
429
+ Individual state will be combined into one big object in the Redux devtools for easy inspection.
430
+
431
+ ```js
432
+ import { createState } from "reactish-state";
433
+ import { reduxDevtools } from "reactish-state/middleware";
434
+
435
+ const state = createState({ middleware: reduxDevtools({ name: "todoApp" }) });
436
+
437
+ const todos = state(
438
+ [],
439
+ (set) => ({
440
+ add: (task) =>
441
+ set(
442
+ (todos) => {
443
+ /* Add todo */
444
+ },
445
+ // Log action type in the second parameter of `set`
446
+ "todo/add"
447
+ ),
448
+ toggle: (id) =>
449
+ set(
450
+ (todos) => {
451
+ /* Toggle todo */
452
+ },
453
+ // You can also log action type along with its payload
454
+ { type: "todo/toggle", id }
455
+ )
456
+ }),
457
+ // Similar to the persist middleware, give each state a unique key
458
+ { key: "todos" }
459
+ );
460
+
461
+ // `todos` and `filter` will be combined into one state in the Redux devtools
462
+ const filter = state("ALL", null, { key: "filter" });
463
+ ```
464
+
465
+ ## Using multiple middleware
466
+
467
+ Middleware is chain-able. You can use the `applyMiddleware` utility to chain multiple middleware and supply the result to `createState`.
468
+
469
+ ```js
470
+ import { applyMiddleware } from "reactish-state/middleware";
471
+
472
+ const state = createState({
473
+ middleware: applyMiddleware([immer, reduxDevtools(), persist()])
474
+ });
475
+ ```
476
+
477
+ ## Using different middleware in different states
478
+
479
+ This is naturally achievable thanks to the decentralized state model.
480
+
481
+ ```js
482
+ const persistState = createState({ middleware: persist() });
483
+ const immerState = createState({ middleware: immer });
484
+
485
+ const visibilityFilter = persistState("ALL"); // Will be persisted
486
+ const todos = immerState([]); // Can be mutated
487
+ ```
488
+
489
+ It also helps eliminate the need for implementing whitelist/blacklist in a persist middleware.
490
+
491
+ ## Plugins
492
+
493
+ While the middleware is used to enhance state, you can hook into selectors using the plugins. The main difference is that plugins don't return a `set` function because selectors are read-only. Similarly, you use the `createSelector` export from the library rather than `selector`.
494
+
495
+ ```js
496
+ import { state, createSelector } from "reactish-state";
497
+
498
+ const selector = createSelector({
499
+ plugin: ({ get, subscribe }, config) => {
500
+ subscribe(() => {
501
+ // Log selector value every time after it has changed
502
+ // `config` can hold contextual data from a selector
503
+ console.log(`${config?.key} selector:`, get());
504
+ });
505
+ }
506
+ });
507
+
508
+ const countState = state(0);
509
+ const doubleSelector = selector(
510
+ countState,
511
+ (count) => count * 2,
512
+ // Provide contextual data in the last parameter to identity selector
513
+ {
514
+ key: "double"
515
+ }
516
+ );
517
+ const squareSelector = selector(countState, (count) => count * count, {
518
+ key: "square"
519
+ });
520
+
521
+ countState.set(5); // Will log - double selector: 10, square selector: 25
522
+ ```
523
+
524
+ Likewise, there is an `applyPlugin` function for applying multiple plugins.
525
+
526
+ ## Redux devtools plugin
527
+
528
+ Individual selector will be combined into one big object in the Redux devtools for easy inspection.
529
+
530
+ ```js
531
+ import { createSelector } from "reactish-state";
532
+ import { reduxDevtools } from "reactish-state/plugin";
533
+
534
+ const selector = createSelector({ plugin: reduxDevtools() });
535
+ // Then use the `selector` as always...
536
+ ```
537
+
538
+ # Examples
539
+
540
+ - Counter – [sandbox](https://codesandbox.io/s/reactish-counter-3let0o) | [source](https://github.com/szhsin/reactish-state/tree/master/examples/examples/counter)
541
+ - Todo app – [sandbox](https://codesandbox.io/s/reactish-todo-thyhbl) | [source](https://github.com/szhsin/reactish-state/tree/master/examples/examples/todo)
542
+ - Async – [sandbox](https://codesandbox.io/s/reactish-async-2cghkg) | [source](https://github.com/szhsin/reactish-state/tree/master/examples/examples/async)
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "reactish-state",
3
- "version": "0.10.2",
4
- "description": "",
3
+ "version": "0.10.3",
4
+ "description": "Simple, decentralized state management for React.",
5
5
  "author": "Zheng Song",
6
6
  "license": "MIT",
7
+ "repository": "szhsin/reactish-state",
8
+ "homepage": "https://github.com/szhsin/reactish-state#readme",
7
9
  "main": "./dist/cjs/index.js",
8
10
  "module": "./dist/esm/index.js",
9
11
  "types": "./types/index.d.ts",
@@ -14,6 +16,12 @@
14
16
  "middleware/",
15
17
  "plugin/"
16
18
  ],
19
+ "keywords": [
20
+ "react",
21
+ "state",
22
+ "management",
23
+ "redux"
24
+ ],
17
25
  "scripts": {
18
26
  "start": "run-p watch \"types -- --watch\"",
19
27
  "bundle": "rollup -c",
@@ -65,35 +73,35 @@
65
73
  "use-sync-external-store": "^1.2.0"
66
74
  },
67
75
  "devDependencies": {
68
- "@babel/core": "^7.19.6",
69
- "@babel/preset-env": "^7.19.4",
76
+ "@babel/core": "^7.20.12",
77
+ "@babel/preset-env": "^7.20.2",
70
78
  "@babel/preset-react": "^7.18.6",
71
79
  "@babel/preset-typescript": "^7.18.6",
72
- "@redux-devtools/extension": "^3.2.3",
73
- "@rollup/plugin-babel": "^6.0.0",
74
- "@rollup/plugin-node-resolve": "^15.0.0",
80
+ "@redux-devtools/extension": "^3.2.5",
81
+ "@rollup/plugin-babel": "^6.0.3",
82
+ "@rollup/plugin-node-resolve": "^15.0.1",
75
83
  "@testing-library/jest-dom": "^5.16.5",
76
84
  "@testing-library/react": "^13.4.0",
77
- "@types/jest": "^29.1.2",
78
- "@types/react": "^18.0.24",
85
+ "@types/jest": "^29.2.5",
86
+ "@types/react": "^18.0.26",
79
87
  "@types/use-sync-external-store": "^0.0.3",
80
- "@typescript-eslint/eslint-plugin": "^5.40.1",
81
- "@typescript-eslint/parser": "^5.40.1",
88
+ "@typescript-eslint/eslint-plugin": "^5.48.2",
89
+ "@typescript-eslint/parser": "^5.48.2",
82
90
  "babel-plugin-pure-annotations": "^0.1.2",
83
- "eslint": "^8.25.0",
84
- "eslint-config-prettier": "^8.5.0",
85
- "eslint-plugin-jest": "^27.1.3",
86
- "eslint-plugin-react": "^7.31.10",
91
+ "eslint": "^8.32.0",
92
+ "eslint-config-prettier": "^8.6.0",
93
+ "eslint-plugin-jest": "^27.2.1",
94
+ "eslint-plugin-react": "^7.32.1",
87
95
  "eslint-plugin-react-hooks": "^4.6.0",
88
96
  "eslint-plugin-react-hooks-addons": "^0.3.1",
89
- "immer": "^9.0.16",
90
- "jest": "^29.2.2",
91
- "jest-environment-jsdom": "^29.2.2",
97
+ "immer": "^9.0.18",
98
+ "jest": "^29.3.1",
99
+ "jest-environment-jsdom": "^29.3.1",
92
100
  "npm-run-all": "^4.1.5",
93
- "prettier": "^2.7.1",
101
+ "prettier": "^2.8.3",
94
102
  "react": "^18.2.0",
95
103
  "react-dom": "^18.2.0",
96
- "rollup": "^3.2.3",
97
- "typescript": "^4.8.4"
104
+ "rollup": "^3.10.0",
105
+ "typescript": "^4.9.4"
98
106
  }
99
107
  }
package/types/common.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- export declare type Getter<T> = () => T;
2
- export declare type Setter<T> = (newValue: T | ((value: T) => T), action?: string | {
1
+ export type Getter<T> = () => T;
2
+ export type Setter<T> = (newValue: T | ((value: T) => T), action?: string | {
3
3
  type: string;
4
4
  [key: string]: unknown;
5
5
  }) => void;
6
- export declare type Listener = () => void;
7
- export declare type Subscriber = (listener: Listener) => () => void;
6
+ export type Listener = () => void;
7
+ export type Subscriber = (listener: Listener) => () => void;
8
8
  export interface Reactish<T> {
9
9
  get: Getter<T>;
10
10
  subscribe: Subscriber;
@@ -2,7 +2,7 @@ import type { Middleware } from '../common';
2
2
  interface PersistMiddleware extends Middleware {
3
3
  hydrate(): void;
4
4
  }
5
- declare type Persist = (options?: {
5
+ type Persist = (options?: {
6
6
  prefix?: string;
7
7
  getStorage?: () => Pick<Storage, 'getItem' | 'setItem'>;
8
8
  }) => PersistMiddleware;
@@ -1,5 +1,5 @@
1
1
  import type { Middleware } from '../common';
2
- declare type ReduxDevtools = (options?: {
2
+ type ReduxDevtools = (options?: {
3
3
  name?: string;
4
4
  }) => Middleware | undefined;
5
5
  declare const reduxDevtools: ReduxDevtools;
@@ -1,5 +1,5 @@
1
1
  import type { Plugin } from '../common';
2
- declare type ReduxDevtools = (options?: {
2
+ type ReduxDevtools = (options?: {
3
3
  name?: string;
4
4
  }) => Plugin | undefined;
5
5
  declare const reduxDevtools: ReduxDevtools;
@@ -1,9 +1,9 @@
1
1
  import type { Reactish, Plugin, Config } from '../common';
2
- declare type ReactishArray = Reactish<unknown>[];
3
- declare type ReactishValueArray<R extends ReactishArray> = {
2
+ type ReactishArray = Reactish<unknown>[];
3
+ type ReactishValueArray<R extends ReactishArray> = {
4
4
  [index in keyof R]: ReturnType<R[index]['get']>;
5
5
  };
6
- declare type SelectorFunc<R extends ReactishArray, T> = (...args: ReactishValueArray<R>) => T;
6
+ type SelectorFunc<R extends ReactishArray, T> = (...args: ReactishValueArray<R>) => T;
7
7
  interface Selector {
8
8
  <R extends ReactishArray, T>(...items: [...R, SelectorFunc<R, T>]): Reactish<T>;
9
9
  <R extends ReactishArray, T>(...items: [...R, SelectorFunc<R, T>, Config]): Reactish<T>;
@@ -1,5 +1,5 @@
1
1
  import type { Reactish, Setter, Config, Middleware } from '../common';
2
- declare type ActionCreator<T, A> = ((set: Setter<T>, get: () => T) => A) | null | undefined;
2
+ type ActionCreator<T, A> = ((set: Setter<T>, get: () => T) => A) | null | undefined;
3
3
  interface State<T, A = unknown, C extends ActionCreator<T, A> = undefined> extends Reactish<T> {
4
4
  set: Setter<T>;
5
5
  actions: C extends undefined ? never : A;