Skip to content

Commit 7ac16d8

Browse files
authored
feat: refactor types and add function overloads for state (#51)
1 parent 4a374a6 commit 7ac16d8

35 files changed

Lines changed: 299 additions & 173 deletions

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -648,8 +648,6 @@ const myTodos = state<string[], { add: (newTodo: string) => void }>(
648648
);
649649
```
650650
651-
However, if you choose this method, you need to specify the types for both the state value and actions.
652-
653651
# Examples
654652
655653
- Counter – [sandbox](https://codesandbox.io/p/sandbox/reactish-counter-z42qt7) | [source](https://github.com/szhsin/reactish-state/tree/master/examples/examples/counter)

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "reactish-state",
3-
"version": "1.1.1",
3+
"version": "1.2.0",
44
"description": "Simple, decentralized (atomic) state management for React.",
55
"author": "Zheng Song",
66
"license": "MIT",

src/__tests__/middleware/applyMiddleware.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Middleware } from '../../common';
1+
import type { Middleware } from '../../types';
22
import { createState } from '../../';
33
import { applyMiddleware } from '../../middleware';
44

src/__tests__/react/useSelector-mock.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44
import * as React from 'react';
55
import { render, screen, fireEvent } from '@testing-library/react';
6-
import { Selector } from '../../common';
6+
import { Selector } from '../../types';
77
import { state, useSelector, useSnapshot } from '../../';
88

99
jest.mock('../../react/useSnapshot', () => ({

src/__tests__/vanilla/selector.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { state } from '../../vanilla/state';
2-
import { selector, createSelector } from '../../';
2+
import { selector, createSelector, Config } from '../../';
33

44
test('selector should update when the base state has changed', () => {
55
const price = state(7);
@@ -61,7 +61,7 @@ test('selector should return cached result when base state has not changed', ()
6161

6262
test('selector can be enhanced with plugin', () => {
6363
const plugin = jest.fn();
64-
const selector = createSelector({
64+
const selector = createSelector<Config>({
6565
plugin: ({ get, subscribe }, config) => {
6666
const onChange = () => {
6767
plugin(get(), config?.key);

src/__tests__/vanilla/state.test.ts

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,86 @@
1-
import { state, createState } from '../../';
1+
import { state, createState, Config } from '../../';
2+
3+
test('function overloads and type correctness', () => {
4+
const s1 = state('s1');
5+
expect(s1.get().trim()).toBe('s1');
6+
7+
const s2 = state<string>();
8+
expect(s2.get()?.trim()).toBeUndefined();
9+
10+
const s2ctx = state<string, number>();
11+
expect(s2ctx.get()?.trim()).toBeUndefined();
12+
s2ctx.set('context', 1);
13+
14+
const s3 = state([] as string[]);
15+
expect(s3.get()[0]?.trim()).toBeUndefined();
16+
17+
const s4 = state([] as string[], (set, get) => ({
18+
append: (newValue: string) => set([...get(), newValue])
19+
}));
20+
s4.append('s4');
21+
expect(s4.get()[0].trim()).toBe('s4');
22+
23+
// Custom actions must not override built-in state properties
24+
const s5 = state([] as string[], (set, get) => ({
25+
append: (newValue: string) => set([...get(), newValue]),
26+
set: () => set([])
27+
}));
28+
s5.append('tmp');
29+
expect(s5.get()).toEqual(['tmp']);
30+
s5.set(['s5']);
31+
expect(s5.get()[0].trim()).toBe('s5');
32+
33+
const s6 = state<string[], { append: (newValue: string) => void }>([], (set, get) => ({
34+
append: (newValue) => set([...get(), newValue])
35+
}));
36+
s6.append('s6');
37+
expect(s6.get()[0].trim()).toBe('s6');
38+
39+
const s6ctx = state<string[], { append: (newValue: string) => void }, number>([], (set, get) => ({
40+
append: (newValue) => set([...get(), newValue])
41+
}));
42+
s6ctx.append('s6');
43+
expect(s6ctx.get()[0].trim()).toBe('s6');
44+
s6ctx.set(['context'], 1);
45+
46+
const s7 = state<string[]>([], null, { key: 's7' });
47+
expect(s7.get()[0]?.trim()).toBeUndefined();
48+
49+
const s7ctx = state<string[], number>([], null, { key: 's7' });
50+
expect(s7ctx.get()[0]?.trim()).toBeUndefined();
51+
s7ctx.set(['context'], 1);
52+
53+
const s8 = state<string | undefined>(undefined, undefined, { key: 's8' });
54+
expect(s8.get()?.trim()).toBeUndefined();
55+
56+
const s8ctx = state<string | undefined, number>(undefined, undefined, { key: 's8' });
57+
expect(s8ctx.get()?.trim()).toBeUndefined();
58+
s8ctx.set('context', 1);
59+
60+
const s9 = state<string[], { append: (newValue: string) => void }>(
61+
[],
62+
(set, get) => ({
63+
append: (newValue) => set([...get(), newValue])
64+
}),
65+
{ key: 's9' }
66+
);
67+
s9.append('s9');
68+
expect(s9.get()[0].trim()).toBe('s9');
69+
70+
const s9ctx = state<string[], { append: (newValue: string) => void }, number>(
71+
[],
72+
(set, get) => ({
73+
append: (newValue) => set([...get(), newValue])
74+
}),
75+
{ key: 's9' }
76+
);
77+
s9ctx.append('s9');
78+
expect(s9ctx.get()[0].trim()).toBe('s9');
79+
s9ctx.set(['context'], 1);
80+
81+
const s10 = state('s10', undefined);
82+
expect(s10.get().trim()).toBe('s10');
83+
});
284

385
test('state should notify listeners when updated', () => {
486
const listener = jest.fn();
@@ -66,7 +148,7 @@ test('state can bind actions', () => {
66148

67149
test('state can be enhanced with middleware', () => {
68150
const middleware = jest.fn();
69-
const state = createState({
151+
const state = createState<Config>({
70152
middleware:
71153
({ set, get }, config) =>
72154
(...arg) => {

src/common.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export * from './common';
1+
export * from './types';
22
export * from './vanilla/state';
33
export * from './vanilla/selector';
44
export * from './react/useSnapshot';

src/middleware/applyMiddleware.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import type { Middleware } from '../common';
1+
import type { Middleware } from '../types';
22

3-
const applyMiddleware: (
4-
middlewares: (Middleware | undefined)[],
3+
const applyMiddleware: <TConfig>(
4+
middlewares: (Middleware<TConfig> | undefined)[],
55
options?: { fromRight?: boolean }
6-
) => Middleware =
6+
) => Middleware<TConfig> =
77
(middlewares, { fromRight } = {}) =>
88
(api, config) =>
99
middlewares[fromRight ? 'reduceRight' : 'reduce'](

0 commit comments

Comments
 (0)