opshot 0.5.3 → 0.6.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/README.md +83 -24
- package/dist/chunk-4JZIOHSP.js +768 -0
- package/dist/createMutableState-DJ9dmpR-.d.ts +36 -0
- package/dist/index.d.ts +52 -76
- package/dist/index.js +7 -1255
- package/dist/react.d.ts +23 -0
- package/dist/react.js +517 -0
- package/package.json +10 -1
package/README.md
CHANGED
|
@@ -82,7 +82,8 @@ This is how you optimize re-rendering across your component tree: place `scope`
|
|
|
82
82
|
## Creating State
|
|
83
83
|
|
|
84
84
|
```tsx
|
|
85
|
-
import { ignore, unsafeTrack
|
|
85
|
+
import { ignore, unsafeTrack } from "opshot";
|
|
86
|
+
import { useMutableState } from "opshot/react";
|
|
86
87
|
|
|
87
88
|
interface PlayerState {
|
|
88
89
|
position: number;
|
|
@@ -114,6 +115,10 @@ const Player = () => {
|
|
|
114
115
|
};
|
|
115
116
|
```
|
|
116
117
|
|
|
118
|
+
`createMutableState(properties, options)` creates the same state outside a component, for a store or a process with no React.
|
|
119
|
+
|
|
120
|
+
`ignore(value, false)` and `unsafeTrack(value, false)` undo these effects on the value.
|
|
121
|
+
|
|
117
122
|
## Constraints
|
|
118
123
|
|
|
119
124
|
opshot tracks plain data.
|
|
@@ -127,14 +132,13 @@ It can't track:
|
|
|
127
132
|
|
|
128
133
|
By default opshot throws when it meets one of these, naming the value that caused it. Passing `strict: false` turns off those errors but may cause unpredictable behaviour.
|
|
129
134
|
|
|
130
|
-
`ignore(value)` stores a value without state inside it being tracked, and `ignore(value, false)` undoes that. `unsafeTrack(value)` does the reverse: it takes a value strict mode would reject, tracking the plain data on it and quietly missing the rest. Either mark only affects states the value enters afterwards.
|
|
131
|
-
|
|
132
135
|
## Tracked collections
|
|
133
136
|
|
|
134
137
|
`TrackedMap`, `TrackedSet`, and `TrackedDate` stand in for the built-ins opshot rejects. They have the exact same API as their counterparts.
|
|
135
138
|
|
|
136
139
|
```ts
|
|
137
|
-
import { TrackedMap
|
|
140
|
+
import { TrackedMap } from "opshot";
|
|
141
|
+
import { useMutableState } from "opshot/react";
|
|
138
142
|
|
|
139
143
|
const state = useMutableState({ index: new TrackedMap<string, number>() });
|
|
140
144
|
|
|
@@ -147,15 +151,20 @@ state.index.set("a", 1);
|
|
|
147
151
|
|
|
148
152
|
```tsx
|
|
149
153
|
import { useEffect } from "react";
|
|
150
|
-
import { subscribe
|
|
154
|
+
import { subscribe } from "opshot";
|
|
155
|
+
import { useMutableState } from "opshot/react";
|
|
156
|
+
|
|
157
|
+
interface Meta {
|
|
158
|
+
source: string;
|
|
159
|
+
}
|
|
151
160
|
|
|
152
161
|
const Counter = () => {
|
|
153
162
|
const counter = useMutableState({ count: 0 });
|
|
154
163
|
|
|
155
164
|
useEffect(
|
|
156
165
|
() =>
|
|
157
|
-
subscribe(counter, (operations) => {
|
|
158
|
-
// operations: [{ node, key: "count", before: 0, after: 1, meta: undefined }]
|
|
166
|
+
subscribe<Meta | undefined>(counter, (operations) => {
|
|
167
|
+
// operations: [{ kind: "change", node, key: "count", before: 0, after: 1, meta: undefined }]
|
|
159
168
|
}),
|
|
160
169
|
[counter],
|
|
161
170
|
);
|
|
@@ -169,26 +178,58 @@ const Counter = () => {
|
|
|
169
178
|
An operation is one key's change on one node:
|
|
170
179
|
|
|
171
180
|
```ts
|
|
172
|
-
interface
|
|
173
|
-
readonly
|
|
181
|
+
interface AddOperation<Meta = unknown> {
|
|
182
|
+
readonly kind: "add";
|
|
183
|
+
readonly node: Record<string, unknown>; // The live state being applied to
|
|
184
|
+
readonly key: string;
|
|
185
|
+
readonly after: unknown;
|
|
186
|
+
readonly meta: Meta;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
interface ChangeOperation<Meta = unknown> {
|
|
190
|
+
readonly kind: "change";
|
|
191
|
+
readonly node: Record<string, unknown>;
|
|
192
|
+
readonly key: string;
|
|
193
|
+
readonly before: unknown;
|
|
194
|
+
readonly after: unknown;
|
|
195
|
+
readonly meta: Meta;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface DeleteOperation<Meta = unknown> {
|
|
199
|
+
readonly kind: "delete";
|
|
200
|
+
readonly node: Record<string, unknown>;
|
|
174
201
|
readonly key: string;
|
|
175
|
-
readonly before
|
|
176
|
-
readonly
|
|
177
|
-
readonly meta: unknown;
|
|
202
|
+
readonly before: unknown;
|
|
203
|
+
readonly meta: Meta;
|
|
178
204
|
}
|
|
205
|
+
|
|
206
|
+
type Operation<Meta = unknown> = AddOperation<Meta> | ChangeOperation<Meta> | DeleteOperation<Meta>;
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
const revert = (operation: Operation) => {
|
|
211
|
+
if (operation.kind === "add") delete operation.node[operation.key];
|
|
212
|
+
else operation.node[operation.key] = operation.before;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const apply = (operation: Operation) => {
|
|
216
|
+
if (operation.kind === "delete") delete operation.node[operation.key];
|
|
217
|
+
else operation.node[operation.key] = operation.after;
|
|
218
|
+
};
|
|
179
219
|
```
|
|
180
220
|
|
|
181
|
-
|
|
221
|
+
Undo and redo are a switch on `kind`. Revert an emission in reverse and apply it forward.
|
|
182
222
|
|
|
183
223
|
## Emission
|
|
184
224
|
|
|
185
|
-
A state gathers its writes and delivers them together. The window is a microtask by default, so everything you change in one go arrives as one emission carrying the net change — a listener hears where a field ended up, not every step it took there.
|
|
225
|
+
A state gathers its writes and delivers them together, in order. The window is a microtask by default, so everything you change in one go arrives as one emission carrying the net change — a listener hears where a field ended up, not every step it took there.
|
|
186
226
|
|
|
187
227
|
`emitOn` sets the window instead. opshot hands you a `flush`, and the state waits until you call it.
|
|
188
228
|
|
|
189
229
|
```tsx
|
|
190
230
|
import { useEffect } from "react";
|
|
191
|
-
import { subscribe
|
|
231
|
+
import { subscribe } from "opshot";
|
|
232
|
+
import { useMutableState } from "opshot/react";
|
|
192
233
|
|
|
193
234
|
const Chart = () => {
|
|
194
235
|
// One emission per frame, however many writes land in between.
|
|
@@ -206,7 +247,7 @@ const Chart = () => {
|
|
|
206
247
|
};
|
|
207
248
|
```
|
|
208
249
|
|
|
209
|
-
Separate from that callback, the `flush(state)` export ends
|
|
250
|
+
Separate from that callback, the `flush(state, ...states)` export ends each state's window from outside.
|
|
210
251
|
|
|
211
252
|
## Batches
|
|
212
253
|
|
|
@@ -214,7 +255,8 @@ Separate from that callback, the `flush(state)` export ends the window from outs
|
|
|
214
255
|
|
|
215
256
|
```tsx
|
|
216
257
|
import { useEffect } from "react";
|
|
217
|
-
import { batch, subscribe
|
|
258
|
+
import { batch, subscribe } from "opshot";
|
|
259
|
+
import { useMutableState } from "opshot/react";
|
|
218
260
|
|
|
219
261
|
const TitleBar = () => {
|
|
220
262
|
const doc = useMutableState({ title: "Untitled" });
|
|
@@ -222,7 +264,7 @@ const TitleBar = () => {
|
|
|
222
264
|
useEffect(
|
|
223
265
|
() =>
|
|
224
266
|
subscribe(doc, (operations) => {
|
|
225
|
-
|
|
267
|
+
const edits = operations.filter((operation) => operation.meta !== "replay");
|
|
226
268
|
|
|
227
269
|
// ...
|
|
228
270
|
}),
|
|
@@ -230,18 +272,35 @@ const TitleBar = () => {
|
|
|
230
272
|
);
|
|
231
273
|
|
|
232
274
|
const rename = () => {
|
|
233
|
-
batch(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
},
|
|
237
|
-
{ source: "editor" },
|
|
238
|
-
);
|
|
275
|
+
batch(() => {
|
|
276
|
+
doc.title = "Draft";
|
|
277
|
+
}, "editor");
|
|
239
278
|
};
|
|
240
279
|
|
|
241
280
|
// ...
|
|
242
281
|
};
|
|
243
282
|
```
|
|
244
283
|
|
|
284
|
+
## Identity
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
const object = { name: "Ada", age: 36 };
|
|
288
|
+
const state = useMutableState(object);
|
|
289
|
+
|
|
290
|
+
object === state; // false
|
|
291
|
+
isSameIdentity(object, state); // true
|
|
292
|
+
|
|
293
|
+
isState(object); // false
|
|
294
|
+
isState(state); // true
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
```tsx
|
|
298
|
+
// The state's identity changes when the state or anything nested inside it changes.
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
// ...
|
|
301
|
+
}, [state]);
|
|
302
|
+
```
|
|
303
|
+
|
|
245
304
|
## License
|
|
246
305
|
|
|
247
306
|
[MIT](LICENSE)
|