Skip to content

Commit 2eb2780

Browse files
committed
add subscribe/notify feature.
1 parent aefcbd2 commit 2eb2780

File tree

8 files changed

+140
-30
lines changed

8 files changed

+140
-30
lines changed

README.md

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,90 @@ Live demo: https://yesil.github.io/picosm
44

55
## Introduction
66

7-
This is an experimental lightweight, zero dependency state manager that attempts to replicate core MobX features without using Proxy objects.
7+
Pico State Manager is an experimental, lightweight, zero-dependency state manager that replicates core MobX features without using Proxy objects.
88

99
It also provides a helper function to make Lit components observers and updates them on changes.
1010

11-
Currently, the non minified bundle size is `4938 bytes` and is around `~1.2kb` gzipped.
11+
Currently, the non-minified bundle size is `5992 bytes` and around `1536 bytes` gzipped.
1212

13-
## Partially Supported Features
13+
You can also import only the specific modules you need from the source to further reduce the size.
1414

15-
- `makeObservable`: generates a new class whose instances are observable
16-
- `observe`: callback when the instance of an observable class changes
17-
- `reaction`: react to specific changes
18-
- `computed`: cache computed values
15+
## Partially Supported Features
1916

20-
Also, an opinionated approach for tracking nested dependencies with `track` functions is added. <br>
21-
If an observable wants to get notified by another one, these functions can be used.
17+
- `makeObservable`: Generates a new class whose instances are observable.
18+
- `observe`: Callback when the instance of an observable class changes.
19+
- `reaction`: React to specific changes.
20+
- `track`: Tracks other observables and notifies own observers on change.
21+
- `subscribe`: Subscribe to changes in an observable.
22+
- `notify`: Notify all subscribers of changes.
23+
- `computed`: Cache computed values.
2224

2325
## How to Use
2426

25-
Add the picosm dependency
27+
Add the picosm dependency:
2628

2729
```bash
28-
npm add https://github.com/yesil/picosm/releases/download/v1.0.2/picosm-1.0.2.tgz
30+
npm add https://github.com/yesil/picosm/releases/download/v1.0.3/picosm-1.0.3.tgz
2931
```
3032

3133
See the unit test: https://github.com/yesil/picosm/blob/main/test/LitObserver.test.js <br>
3234
See the demo app source code: https://github.com/yesil/picosm/tree/main/app for a more comprehensive example.
3335

36+
## API Documentation
37+
38+
### `makeObservable`
39+
40+
Generates a new class whose instances are observable.
41+
42+
### `observe`
43+
44+
Creates an observer for a given observable.
45+
46+
Returns a `disposer` function.
47+
48+
### `reaction`
49+
50+
React to specific changes.
51+
52+
Returns a `disposer` function.
53+
54+
### `track`
55+
56+
Tracks other observables and notifies own observers on change.
57+
58+
Returns a `disposer` function.
59+
60+
### `subscribe`
61+
62+
An observable becomes automatically a subscription queue, where one can register several subscriptions for receiving arbitrary messages.
63+
64+
Returns a `disposer` function.
65+
66+
#### Example
67+
68+
```javascript
69+
const disposer = subscribe(observableInstance, (message) => {
70+
console.log('Message received', message);
71+
});
72+
```
73+
74+
### `notify`
75+
76+
Notify all subscribers of changes.
77+
78+
#### Example
79+
80+
```javascript
81+
notify(observableInstance, message);
82+
```
83+
84+
### `computed`
85+
86+
Cache computed values.
87+
3488
## Contributing
3589

3690
Please feel free to:
3791

38-
Open a PR to contribute.
39-
Create an issue to request a feature.
92+
- Open a PR to contribute.
93+
- Create an issue to request a feature.

dist/picosm.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,23 @@ function makeObservable(constructor2, actions = [], computeds = []) {
7676
this.__observers.delete(callback);
7777
};
7878
}
79+
__subscribe(onMessageCallback) {
80+
if (!this.__subscribers) {
81+
Object.defineProperties(
82+
this,
83+
{
84+
__subscribers: { value: /* @__PURE__ */ new Set() }
85+
},
86+
{
87+
__subscribers: { enumerable: false, writable: false }
88+
}
89+
);
90+
}
91+
this.__subscribers.add(onMessageCallback);
92+
return () => {
93+
this.__subscribers.delete(onMessageCallback);
94+
};
95+
}
7996
}
8097
actions.forEach((methodName) => {
8198
instrumentAction(SuperClass, methodName);
@@ -100,6 +117,14 @@ function observe(target, callback, timeout) {
100117
return target.__observe(callback);
101118
}
102119
}
120+
function subscribe(target, onMessageCallback) {
121+
return target.__subscribe(onMessageCallback);
122+
}
123+
function notify(target, message) {
124+
target.__subscribers?.forEach((listener) => {
125+
listener(message);
126+
});
127+
}
103128

104129
// src/reaction.js
105130
function reaction(target, callback, execute, timeout) {
@@ -134,6 +159,7 @@ function track(target, source) {
134159
});
135160
target.__resetComputedProperties();
136161
target.__notifyObservers();
162+
return disposer;
137163
}
138164

139165
// src/LitObserver.js
@@ -182,7 +208,9 @@ function litObserver(constructor, properties) {
182208
export {
183209
litObserver,
184210
makeObservable,
211+
notify,
185212
observe,
186213
reaction,
214+
subscribe,
187215
track
188216
};

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": "picosm",
3-
"version": "1.0.2",
3+
"version": "1.0.3",
44
"main": "dist/picosm.js",
55
"scripts": {
66
"test": "web-test-runner test/**/*.test.js --node-resolve",

src/makeObservable.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,24 @@ export function makeObservable(constructor, actions = [], computeds = []) {
8484
this.__observers.delete(callback);
8585
};
8686
}
87+
88+
__subscribe(onMessageCallback) {
89+
if (!this.__subscribers) {
90+
Object.defineProperties(
91+
this,
92+
{
93+
__subscribers: { value: new Set() },
94+
},
95+
{
96+
__subscribers: { enumerable: false, writable: false },
97+
},
98+
);
99+
}
100+
this.__subscribers.add(onMessageCallback);
101+
return () => {
102+
this.__subscribers.delete(onMessageCallback);
103+
};
104+
}
87105
}
88106

89107
actions.forEach((methodName) => {
@@ -111,3 +129,13 @@ export function observe(target, callback, timeout) {
111129
return target.__observe(callback);
112130
}
113131
}
132+
133+
export function subscribe(target, onMessageCallback) {
134+
return target.__subscribe(onMessageCallback);
135+
}
136+
137+
export function notify(target, message) {
138+
target.__subscribers?.forEach((listener) => {
139+
listener(message);
140+
});
141+
}

src/track.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export function track(target, source) {
55
});
66
target.__resetComputedProperties();
77
target.__notifyObservers();
8+
return disposer;
89
}

test/TestStore.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,37 @@ import { track } from '../src/track.js';
22

33
export default class TestStore {
44
#disposer;
5+
#connection;
6+
#counter = 0;
7+
58
constructor() {
69
this.checked = false;
7-
this._counter = 0;
8-
this._test = undefined;
910
}
1011

1112
toggleCheck() {
1213
this.checked = !this.checked;
13-
this._counter++;
14+
this.#counter++;
1415
}
1516

1617
async toggleAsyncCheck() {
1718
await new Promise((resolve) =>
1819
setTimeout(() => {
1920
this.checked = !this.checked;
20-
this._counter++;
21+
this.#counter++;
2122
resolve(true);
2223
}, 100),
2324
);
2425
}
2526

2627
get counter() {
27-
return this._counter + (this._test ? this._test.counter : 0);
28+
return this.#counter + (this.#connection ? this.#connection.counter : 0);
2829
}
2930

30-
set test(test) {
31-
if (this._test) {
32-
this.#disposer?.();
33-
}
34-
this._test = test;
35-
if (test) {
36-
this.#disposer = track(this, test);
31+
connect(store) {
32+
this.#disposer?.();
33+
this.#connection = store;
34+
if (store) {
35+
this.#disposer = track(this, store);
3736
}
3837
}
3938

test/track.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ describe('Pico State Manager', function () {
1919
expect(test1.counter).to.equal(1);
2020
expect(test2.counter).to.equal(1);
2121

22-
test1.test = test2;
22+
test1.connect(test2);
2323
test2.toggleCheck();
2424

2525
expect(test1.counter).to.equal(3);
2626

27-
test1.test = undefined;
27+
test1.connect();
2828
test2.toggleCheck();
2929
});
3030
});

0 commit comments

Comments
 (0)